char source[] = "<FileSystem>\n"
"\t<directory name=\"babyxfs_src\">\n"
"\t\t<file name=\"asciitostring.h\" type=\"text\">\n"
"#ifndef asciitostring_h\n"
"#define asciitostring_h\n"
"\n"
"char *fslurp(FILE *fp);\n"
"int validcstring(const char *str);\n"
"char *texttostring(const char *str);\n"
"\n"
"#endif\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"testbabyxfilesystem.c\" type=\"text\">\n"
"//\n"
"//  testbabyxfilesystem.c\n"
"//  testbabyxfilesystem\n"
"//\n"
"//  Created by Malcolm McLean on 31/05/2024.\n"
"//\n"
"\n"
"#include &lt;stdio.h&gt;\n"
"#include &lt;stdlib.h&gt;\n"
"#include &lt;string.h&gt;\n"
"#include &lt;limits.h&gt;\n"
"\n"
"#include &quot;bbx_filesystem.h&quot;\n"
"\n"
"/*\n"
"  load a text file into memory\n"
"\n"
"*/\n"
"static char *fslurp(FILE *fp)\n"
"{\n"
"  char *answer;\n"
"  char *temp;\n"
"  int buffsize = 1024;\n"
"  int i = 0;\n"
"  int ch;\n"
"\n"
"  answer = malloc(1024);\n"
"  if(!answer)\n"
"    return 0;\n"
"  while( (ch = fgetc(fp)) != EOF )\n"
"  {\n"
"    if(i == buffsize-2)\n"
"    {\n"
"      if(buffsize &gt; INT_MAX - 100 - buffsize/10)\n"
"      {\n"
"    free(answer);\n"
"        return 0;\n"
"      }\n"
"      buffsize = buffsize + 100 * buffsize/10;\n"
"      temp = realloc(answer, buffsize);\n"
"      if(temp == 0)\n"
"      {\n"
"        free(answer);\n"
"        return 0;\n"
"      }\n"
"      answer = temp;\n"
"    }\n"
"    answer[i++] = (char) ch;\n"
"  }\n"
"  answer[i++] = 0;\n"
"\n"
"  temp = realloc(answer, i);\n"
"  if(temp)\n"
"    return temp;\n"
"  else\n"
"    return answer;\n"
"}\n"
"\n"
"static int streamsidentical(FILE *fpa, FILE *fpb)\n"
"{\n"
"    int cha, chb;\n"
"    \n"
"    while ( (cha = fgetc(fpa)) != EOF)\n"
"    {\n"
"        chb = fgetc(fpb);\n"
"        if (cha != chb)\n"
"            return 0;\n"
"    }\n"
"    chb = fgetc(fpb);\n"
"    if (cha != chb)\n"
"        return 0;\n"
"    \n"
"    return 1;\n"
"}\n"
"\n"
"void usage()\n"
"{\n"
"    fprintf(stderr, &quot;testbabyxfilesystem Tests the Baby X file system BBX_FileSystem\\n&quot;);\n"
"    fprintf(stderr, &quot;\\n&quot;);\n"
"    fprintf(stderr, &quot;Usage: testbabyxfilesystem &lt;directory&gt; &lt;directory.xml&gt; &lt;pat"
"h&gt;\\n&quot;);\n"
"    fprintf(stderr, &quot;\\t&lt;directory&gt; - path to the local directory to mount.\\n&quot;);\n"
"    fprintf(stderr, &quot;\\t&lt;directory.xml&gt; - FileSystem XML generated from the directory.\\n&q"
"uot;);\n"
"    fprintf(stderr, &quot;\\t&lt;path&gt; - path to file in the directory to extract.\\n&quot;);\n"
"    fprintf(stderr, &quot;\\t\\tpath in the form \\&quot;/directory/subfolder/readme.txt\\&quot;.\\n&quot"
";);\n"
"    fprintf(stderr, &quot;\\t\\tpass \\&quot;-getname\\&quot; for the path to get the filesystem names.\\"
"n&quot;);\n"
"    fprintf(stderr, &quot;\\n&quot;);\n"
"    fprintf(stderr, &quot;By Malcolm McLean\\n&quot;);\n"
"    fprintf(stderr, &quot;Part of the BabyX project.\\n&quot;);\n"
"    fprintf(stderr, &quot;Check us out on github and get involved.\\n&quot;);\n"
"    fprintf(stderr, &quot;Program and source free to anyone for any use.\\n&quot;);\n"
"    exit(EXIT_FAILURE);\n"
"}\n"
"\n"
"int xmain(int argc, char **argv)\n"
"{\n"
"    BBX_FileSystem *bbx_fs_xml = 0;\n"
"    BBX_FileSystem *bbx_fs_stdio = 0;\n"
"    FILE *fp;\n"
"    FILE *fp_stdio = 0;\n"
"    FILE *fp_xml = 0;\n"
"    int ch;\n"
"    char *xmlstring;\n"
"    int err;\n"
"    if (argc != 4)\n"
"    {\n"
"        usage();\n"
"    }\n"
"    \n"
"    fp = fopen(argv[2], &quot;r&quot;);\n"
"    if (!fp)\n"
"    {\n"
"        fprintf(stderr, &quot;Can&apos;t open xml file\\n&quot;);\n"
"        exit(EXIT_FAILURE);\n"
"    }\n"
"    xmlstring = fslurp(fp);\n"
"    if (!xmlstring)\n"
"    {\n"
"        fprintf(stderr, &quot;Out of memory\\n&quot;);\n"
"        exit(EXIT_FAILURE);\n"
"    }\n"
"    fclose(fp);\n"
"    fp = 0;\n"
"    \n"
"    bbx_fs_stdio = bbx_filesystem();\n"
"    /* no need to check in Baby X, Baby X functions never return out of memory */\n"
"    /* Comment one of these in and out to toggle between filesystems */\n"
"    err = bbx_filesystem_set(bbx_fs_stdio, argv[1], BBX_FS_STDIO);\n"
"    if (err)\n"
"    {\n"
"        fprintf(stderr, &quot;Can&apos;t set up stdio filessystem\\n&quot;);\n"
"        exit(EXIT_FAILURE);\n"
"    }\n"
"    bbx_fs_xml = bbx_filesystem();\n"
"    err = bbx_filesystem_set(bbx_fs_xml, xmlstring, BBX_FS_STRING);\n"
"    if (err)\n"
"    {\n"
"        fprintf(stderr, &quot;Can&apos;t set up XML filessystem\\n&quot;);\n"
"        exit(EXIT_FAILURE);\n"
"    }\n"
"   \n"
"    if (!strcmp(argv[3], &quot;-getname&quot;))\n"
"    {\n"
"        printf(&quot;stdio filesystem name %s\\n&quot;, bbx_filesystem_getname(bbx_fs_stdio));\n"
"        printf(&quot;xml filesystem name %s\\n&quot;, bbx_filesystem_getname(bbx_fs_xml));\n"
"    }\n"
"    else\n"
"    {\n"
"        fp_stdio = bbx_filesystem_fopen(bbx_fs_stdio, argv[3], &quot;r&quot;);\n"
"        if (!fp_stdio)\n"
"        {\n"
"            fprintf(stderr, &quot;Can&apos;t open target file on stdio system\\n&quot;);\n"
"            exit(EXIT_FAILURE);\n"
"        }\n"
"        fp_xml = bbx_filesystem_fopen(bbx_fs_xml, argv[3], &quot;r&quot;);\n"
"        if (!fp_xml)\n"
"        {\n"
"            fprintf(stderr, &quot;Can&apos;t open target file on xml system\\n&quot;);\n"
"            exit(EXIT_FAILURE);\n"
"        }\n"
"        if (!streamsidentical(fp_stdio, fp_xml))\n"
"        {\n"
"            fprintf(stderr, &quot;Files differ\\n&quot;);\n"
"        }\n"
"        fseek(fp_xml, 0, SEEK_SET);\n"
"        while ( (ch = fgetc(fp_xml)) != EOF)\n"
"            putchar(ch);\n"
"        err = bbx_filesystem_fclose(bbx_fs_stdio, fp_stdio);\n"
"        if (err)\n"
"            fprintf(stderr, &quot;Error closing stdio file\\n&quot;);\n"
"        err = bbx_filesystem_fclose(bbx_fs_xml, fp_xml);\n"
"        if (err)\n"
"            fprintf(stderr, &quot;error closing xml file\\n&quot;);\n"
"    }\n"
"    \n"
"    bbx_filesystem_kill(bbx_fs_stdio);\n"
"    bbx_filesystem_kill(bbx_fs_xml);\n"
"    free(xmlstring);\n"
"    \n"
"    return 0;\n"
"}\n"
"\n"
"#include &quot;bbx_write_source.h&quot;\n"
"extern char source[];\n"
"\n"
"int main(void)\n"
"{\n"
"    bbx_write_source(source, &quot;./src/&quot;, &quot;source.c&quot;, &quot;source&quot;);\n"
"}\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"listdirectory.c\" type=\"text\">\n"
"//\n"
"//  listdirectory.c\n"
"//  directorytoxml\n"
"//\n"
"//  Created by Malcolm McLean on 28/05/2024.\n"
"//\n"
"#include &quot;xmlparser2.h&quot;\n"
"#include &lt;stdio.h&gt;\n"
"#include &lt;stdlib.h&gt;\n"
"#include &lt;string.h&gt;\n"
"#include &lt;ctype.h&gt;\n"
"\n"
"/*\n"
"    This program is designed to show off the capabilities of the XML Parser.\n"
" \n"
"     We generate an XML file which represents a filesystem. Then we query it for files.\n"
"     Unfortunatley there is no way to traverse a physical computer&apos;s filesystem with\n"
"     complete portablity, and so the encoder, directorytoxml, will not run everywhere.\n"
"     But the decoder is completetely portable. And you can incorporate it into your own\n"
"     programs to have embeded files. Use the Baby X resource compiler to convert the XML\n"
"     to a string, then load it using xmldocfromstring.\n"
" \n"
"     The XML is very simple\n"
"     \n"
"     &lt;FileSystem&gt;\n"
"           &lt;directory name=&quot;poems&quot;&gt;\n"
"               &lt;directory name=&quot;Shakespeare&quot;&gt;\n"
"                    &lt;file name=&quot;Sonnet 12&quot; type=&quot;text&quot;&gt;\n"
"                        When do I count the clock that tells the time\?\n"
"                    &lt;/file&gt;\n"
"                &lt;/directory&gt;\n"
"                &lt;directory name=&quot;Blake&quot;&gt;\n"
"                    &lt;file name=&quot;Tyger&quot; tyoe=&quot;text&quot;&gt;\n"
"                            Tyger, tyger, burning bright,\n"
"                            Through the forests of the night,\n"
"                    &lt;/file&gt;\n"
"                    &lt;file name=&quot;BlakePicture.png&quot; type=&quot;binary&quot;&gt;\n"
" &lt;![CDATA[M)&quot;E3&apos;U@&quot;:H````0#)A$12!``#H`````6(8````056&quot;`0``@&quot;HFV0#!52#-$(\n"
" M0)W;FE&amp;;E!``(E8E7&gt;`43EN%&apos;_[&gt;3/D`$2(&quot;(E0-4$D.!0*A&gt;H((=04)D$(A\n"
" M2,$(HBHB(+N&quot;L60$1PR*ZJBH@K*UU*6P&quot;+*6PN;06$1==Q&quot;V0EW-P08W]-OW\n"
" ]]&gt;\n"
"                    &lt;/file&gt;\n"
"                &lt;/directory&gt;\n"
"           &lt;/directory&gt;\n"
"     &lt;/FilesSystem&gt;\n"
" \n"
"     All the code was written by Malcolm  McLean.\n"
"      It is free for any use.\n"
" */\n"
"\n"
"int matchwild(const char *text, const char *glob)\n"
"{\n"
"   int i = 0;\n"
"   int j = 0;\n"
"\n"
"   while (text[j] != 0 &amp;&amp; glob[i] != 0)\n"
"   {\n"
"      if (glob[i] == &apos;\?&apos;)\n"
"           ;\n"
"       else if (glob[i] == &apos;*&apos;)\n"
"           break;\n"
"       else if (glob[i] != text[j])\n"
"          break;\n"
"        i++;\n"
"        j++;\n"
"   }\n"
"   \n"
"   if (text[j] == 0 &amp;&amp; glob[i] == 0)\n"
"       return 1;\n"
"    else if (glob[i] == &apos;*&apos;)\n"
"    {\n"
"        do\n"
"        {\n"
"            if (matchwild(text + j++, glob + i + 1))\n"
"                return 1;\n"
"        } while (text[j-1]);\n"
"    }\n"
"\n"
"   return 0;\n"
"}\n"
"\n"
"char *mystrdup(const char *str)\n"
"{\n"
"    char *answer;\n"
"    \n"
"    answer = malloc(strlen(str) +1);\n"
"    if (answer)\n"
"        strcpy(answer, str);\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"   Does a string consist entirely of white space\? (also treat nulls as white)\n"
" */\n"
"int strwhitespace(const char *str)\n"
"{\n"
"    if (!str)\n"
"        return 1;\n"
"    while (*str)\n"
"    {\n"
"        if (!isspace((unsigned char) *str))\n"
"            return  0;\n"
"        str++;\n"
"    }\n"
"    \n"
"    return 1;\n"
"}\n"
"\n"
"char **cat_string(char **list, const char *str)\n"
"{\n"
"    char **answer = 0;\n"
"    char **temp;\n"
"    int N = 0;\n"
"    int i;\n"
"    \n"
"    if (!list)\n"
"    {\n"
"        answer = malloc(2 * sizeof(char *));\n"
"        answer[1] = 0;\n"
"        answer[0] = mystrdup(str);\n"
"    }\n"
"    else\n"
"    {\n"
"        for (i = 0; list[i]; i++)\n"
"            N++;\n"
"        temp = realloc(list, (N+ 2) * sizeof(char *));\n"
"        answer = temp;\n"
"        answer[N+1] = 0;\n"
"        answer[N] = mystrdup(str);\n"
"    }\n"
"    \n"
"    return answer;;\n"
"}\n"
"\n"
"char **cat_list(char **lista, char **listb)\n"
"{\n"
"    char **answer;\n"
"    int Na = 0;\n"
"    int Nb = 0;\n"
"    int i;\n"
"    \n"
"    if (!lista)\n"
"        return listb;\n"
"    if (!listb)\n"
"        return  lista;\n"
"    for (i = 0; lista[i]; i++)\n"
"        Na++;\n"
"    for (i = 0; listb[i]; i++)\n"
"        Nb++;\n"
"    \n"
"    answer = realloc(lista, (Na + Nb + 1) * sizeof(char **));\n"
"    for (i = 0; listb[i]; i++)\n"
"        answer[Na + i] = listb[i];\n"
"    answer[Na+i] = 0;\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"char *directoryname(const char *path, int pos)\n"
"{\n"
"    int i;\n"
"    int j = 0;\n"
"    int len = 0;\n"
"    char *answer = 0;\n"
"    \n"
"    for(i = pos + 1; path[i]; i++)\n"
"        if(path[i] == &apos;/&apos;)\n"
"            break;\n"
"    len = i - pos - 1;\n"
"    answer = malloc(len + 1);\n"
"    if (!answer)\n"
"        goto out_of_memory;\n"
"    for(i = pos + 1; path[i]; i++)\n"
"    {\n"
"        if(path[i] == &apos;/&apos;)\n"
"            break;\n"
"        answer[j++] = path[i];\n"
"    }\n"
"    answer[j++] = 0;\n"
"    return answer;\n"
"out_of_memory:\n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"char **directory_list_r(XMLNODE *node, const char *glob, int pos)\n"
"{\n"
"    char **answer = 0;\n"
"    int N = 0;\n"
"    const char *nodename;\n"
"    char *nameglob = 0;\n"
"    XMLNODE *child;\n"
"    char **sub;\n"
"    int lastdir;\n"
"    char buff[FILENAME_MAX];\n"
"    \n"
"    nameglob = directoryname(glob, pos);\n"
"    lastdir = glob[pos + strlen(nameglob) + 1] == 0 \? 1 : 0;\n"
"    while (node)\n"
"    {\n"
"        if (!strcmp(xml_gettag(node), &quot;directory&quot;))\n"
"        {\n"
"            nodename = xml_getattribute(node, &quot;name&quot;);\n"
"            if (nodename &amp;&amp; matchwild(nodename, nameglob))\n"
"            {\n"
"                if (lastdir)\n"
"                {\n"
"                    snprintf(buff, FILENAME_MAX, &quot;%s/&quot;, nodename);\n"
"                    answer = cat_string(answer, buff);\n"
"                }\n"
"                else\n"
"                {\n"
"                    sub = directory_list_r(node-&gt;child, glob, pos + (int) strlen(nameglob) + 1);\n"
"                    answer = cat_list(answer, sub);\n"
"                }\n"
"            }\n"
"        }\n"
"        else if(lastdir &amp;&amp; !strcmp(xml_gettag(node), &quot;file&quot;))\n"
"        {\n"
"            nodename = xml_getattribute(node, &quot;name&quot;);\n"
"            if (nodename &amp;&amp; matchwild(nodename, nameglob))\n"
"            {\n"
"                answer = cat_string(answer, nodename);\n"
"            }\n"
"        }\n"
"        \n"
"        node = node-&gt;next;\n"
"    }\n"
"    \n"
"    free(nameglob);\n"
"    return  answer;\n"
"}\n"
"\n"
"char **xml_listdirectory_r(XMLNODE *node, const char *glob)\n"
"{\n"
"    char **answer = 0;\n"
"    while (node)\n"
"    {\n"
"        if (!strcmp(xml_gettag(node), &quot;FileSystem&quot;))\n"
"        {\n"
"            answer = directory_list_r(node-&gt;child, glob, 0);\n"
"            if (answer)\n"
"                break;\n"
"        }\n"
"        if (node-&gt;child)\n"
"        {\n"
"            answer = xml_listdirectory_r(node-&gt;child, glob);\n"
"            if (answer)\n"
"                break;\n"
"        }\n"
"        node = node-&gt;next;\n"
"    }\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"   Query an XML document for files under FileSystem tag.\n"
" \n"
"   You will need to take this function if using the FileSystem to\n"
"     embed files into your own programs.\n"
" \n"
" */\n"
"char **xml_listdirectory(XMLDOC *doc, const char *glob)\n"
"{\n"
"    return xml_listdirectory_r(xml_getroot(doc), glob);\n"
"}\n"
"\n"
"void usage()\n"
"{\n"
"    fprintf(stderr, &quot;listdirectory: ls command for FileSystem XML files\\n&quot;);\n"
"    fprintf(stderr, &quot;Usage: listdirectory &lt;filesystem.xml&gt; &lt;pathtofile&gt;\\n&quot;);\n"
"    fprintf(stderr, &quot;\\n&quot;);\n"
"    fprintf(stderr, &quot;For example, listdirectory poemfiles.xml /poems/Blake/*\\n&quot;);\n"
"    fprintf(stderr, &quot;The XML files poemfiles.xml is FileSystem file which\\n&quot;);\n"
"    fprintf(stderr, &quot;contains poems. The command will list all the poems \\n&quot;);\n"
"    fprintf(stderr, &quot;by Blake\\n&quot;);\n"
"    fprintf(stderr, &quot;Generate the FileSystem files with the program directorytoxml\\n&quot;);\n"
"}\n"
"\n"
"int main(int argc, char **argv)\n"
"{\n"
"    XMLDOC *doc = 0;\n"
"    char error[1024];\n"
"    char **list;\n"
"    int i;\n"
"    \n"
"    if (argc != 3)\n"
"    {\n"
"        usage();\n"
"        return -1;\n"
"    }\n"
"    \n"
"    doc = loadxmldoc(argv[1], error, 1024);\n"
"    if (!doc)\n"
"    {\n"
"        fprintf(stderr, &quot;%s\\n&quot;, error);\n"
"        return -1;\n"
"    }\n"
"    \n"
"    list = xml_listdirectory(doc, argv[2]);\n"
"    if (list)\n"
"    {\n"
"        for (i = 0; list[i]; i++)\n"
"            printf(&quot;%s\\n&quot;, list[i]);\n"
"    }\n"
"    else\n"
"    {\n"
"        fprintf(stderr, &quot;Can&apos;t list %s\\n&quot;, argv[2]);\n"
"    }\n"
"\n"
"    if (list)\n"
"    {\n"
"        for (i = 0; list[i]; i++)\n"
"            free(list[i]);\n"
"        free(list);\n"
"    }\n"
"    \n"
"    killxmldoc(doc);\n"
"    \n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"xmlparser2.c\" type=\"text\">\n"
"/*\n"
" XML parser 2, by Malcolm McLean\n"
" Vanilla XML parser\n"
"*/\n"
"\n"
"#include &lt;stdio.h&gt;\n"
"#include &lt;stdlib.h&gt;\n"
"#include &lt;string.h&gt;\n"
"#include &lt;stdarg.h&gt;\n"
"#include &lt;ctype.h&gt;\n"
"\n"
"#define MAXRECURSIONLIMIT 100\n"
"\n"
"\n"
"typedef struct xmlattribute\n"
"{\n"
"  char *name;                /* attriibute name */\n"
"  char *value;               /* attribute value (without quotes) */\n"
"  struct xmlattribute *next; /* next pointer in linked list */\n"
"} XMLATTRIBUTE;\n"
"\n"
"typedef struct xmlnode\n"
"{\n"
"  char *tag;                 /* tag to identify data type */\n"
"  XMLATTRIBUTE *attributes;  /* attributes */\n"
"  char *data;                /* data as ascii */\n"
"  int position;              /* position of the node within parent&apos;s data string */\n"
"  int lineno;                /* line number of node in document */\n"
"  struct xmlnode *next;      /* sibling node */\n"
"  struct xmlnode *child;     /* first child node */\n"
"} XMLNODE;\n"
"\n"
"typedef struct\n"
"{\n"
"  XMLNODE *root;             /* the root node */\n"
"} XMLDOC;\n"
"\n"
"struct strbuff\n"
"{\n"
"    const char *str;\n"
"    int pos;\n"
"};\n"
"\n"
"struct utf16buff\n"
"{\n"
"    char rack[8];\n"
"    int pos;\n"
"    FILE *fp;\n"
"};\n"
"\n"
"typedef struct\n"
"{\n"
"  char *str;\n"
"  int capacity;\n"
"  int N;\n"
"} STRING;\n"
"\n"
"typedef struct\n"
"{\n"
"  int set;\n"
"  char message[1024];\n"
"    struct lexer *lexer;\n"
"    int recursiondepth;\n"
"} ERROR;\n"
"\n"
"\n"
"typedef struct lexer\n"
"{\n"
"  int (*getch)(void *ptr);\n"
"  void *ptr;\n"
"  int token;\n"
"  int lineno;\n"
"  int columnno;\n"
"  int badmatch;\n"
"  ERROR *err;\n"
"} LEXER;\n"
"\n"
"#define UNKNOWNSHRIEK 1000\n"
"#define COMMENT 1001\n"
"#define CDATA 1002\n"
"#define INCLUDE 1002\n"
"#define IGNORE 1003\n"
"#define DOCTYPE 1004\n"
"#define ELEMENT 1005\n"
"#define ATTLIST 1006\n"
"#define NOTATION 1007\n"
"\n"
"#define FMT_UNKNOWN 0\n"
"#define FMT_UTF8 1\n"
"#define FMT_UTF16LE 2\n"
"#define FMT_UTF16BE 3\n"
"\n"
"static int textencoding(FILE *fp);\n"
"static int fileaccess(void *ptr);\n"
"static int utf16accessbe(void *ptr);\n"
"static int utf16accessle(void *ptr);\n"
"static int bbx_utf8_putch(char *out, int ch);\n"
"static int stringaccess(void *ptr);\n"
"\n"
"void killxmlnode(XMLNODE *node);\n"
"static void killxmlattribute(XMLATTRIBUTE *attr);\n"
"\n"
"static int is_initidentifier(int ch);\n"
"static int is_elementnamech(int ch);\n"
"static int is_attributenamech(int ch);\n"
"\n"
"static int string_init(STRING *s);\n"
"static void string_push(STRING *s, int ch, ERROR *err);\n"
"static void string_concat(STRING *s, const char *str, ERROR *err);\n"
"static char *string_release(STRING *s);\n"
"\n"
"static XMLDOC *xmldocument(LEXER *lex, ERROR *err);\n"
"static XMLNODE *xmlnode(LEXER *lex, ERROR *err);\n"
"static XMLNODE *comment(LEXER *lex, ERROR *err);\n"
"static XMLATTRIBUTE *attributelist(LEXER *lex, ERROR *err);\n"
"static XMLATTRIBUTE *xmlattribute(LEXER *lex, ERROR *err);\n"
"static char *quotedstring(LEXER *lex, ERROR *err);\n"
"static char *textspan(LEXER *lex, ERROR *err);\n"
"static char *cdata(LEXER *lex, ERROR *err);\n"
"static char *processinginstruction(LEXER *lex, ERROR *err);\n"
"static char *attributename(LEXER *lex, ERROR *err);\n"
"static char *elementname(LEXER *lex, ERROR *err);\n"
"static int escapechar(LEXER *lex, ERROR *err);\n"
"static int shriektype(LEXER *lex, ERROR *err);\n"
"static void skipbom(LEXER *lex, ERROR *err);\n"
"static void skipunknowntag(LEXER *lex, ERROR *err);\n"
"static void skipwhitespace(LEXER *lex, ERROR *err);\n"
"\n"
"static void initerror(ERROR *err);\n"
"static void enterrecursion(ERROR *err);\n"
"static void endrecursion(ERROR *err);\n"
"static void reporterror(ERROR *err, const char *fmt, ...);\n"
"\n"
"static void initlexer(LEXER *lex, ERROR *err, int (*getch)(void *), void *ptr);\n"
"static int gettoken(LEXER *lex);\n"
"static int match(LEXER *lex, int token);\n"
"\n"
"static char *mystrdup(const char *str);\n"
"\n"
"\n"
"\n"
"\n"
"XMLDOC *loadxmldoc(const char *filename,char *errormessage, int Nerr)\n"
"{\n"
"   FILE *fp;\n"
"    ERROR error;\n"
"   LEXER lexer;\n"
"   XMLDOC *answer = 0;\n"
"    int encoding;\n"
"    struct utf16buff utf16buf = {0};\n"
"    \n"
"    initerror(&amp;error);\n"
"\n"
"   if (errormessage &amp;&amp; Nerr &gt; 0)\n"
"      errormessage[0] = 0;\n"
"\n"
"   fp = fopen(filename, &quot;r&quot;);\n"
"   if (!fp)\n"
"   {\n"
"      snprintf(errormessage, Nerr, &quot;Can&apos;t open %s&quot;, filename);\n"
"      return 0;\n"
"   }\n"
"   else\n"
"   {\n"
"      encoding = textencoding(fp);\n"
"      if (encoding == FMT_UTF8)\n"
"      {\n"
"          initlexer(&amp;lexer, &amp;error, fileaccess, fp);\n"
"      }\n"
"      else if (encoding == FMT_UTF16BE)\n"
"      {\n"
"          utf16buf.fp  = fp;\n"
"          initlexer(&amp;lexer, &amp;error, utf16accessbe, &amp;utf16buf);\n"
"      }\n"
"       else if (encoding == FMT_UTF16LE)\n"
"       {\n"
"           utf16buf.fp = fp;\n"
"           initlexer(&amp;lexer, &amp;error, utf16accessle, &amp;utf16buf);\n"
"       }\n"
"       else\n"
"       {\n"
"           snprintf(errormessage, Nerr, &quot;Can&apos;t determine text format of %s&quot;, filename"
");\n"
"           return 0;\n"
"       }\n"
"       \n"
"      answer = xmldocument(&amp;lexer, &amp;error);\n"
"      if (error.set)\n"
"      {\n"
"         snprintf(errormessage, Nerr, &quot;%s&quot;, error.message);\n"
"      }\n"
"      return answer;\n"
"   }   \n"
"}\n"
"\n"
"XMLDOC *floadxmldoc(FILE *fp, char *errormessage, int Nerr)\n"
"{\n"
"    ERROR error;\n"
"    LEXER lexer;\n"
"    XMLDOC *answer = 0;\n"
"    int encoding;\n"
"    struct utf16buff utf16buf = {0};\n"
"\n"
"    initerror(&amp;error);\n"
"\n"
"    if (errormessage &amp;&amp; Nerr &gt; 0)\n"
"       errormessage[0] = 0;\n"
"    \n"
"    encoding = textencoding(fp);\n"
"    if (encoding == FMT_UTF8)\n"
"    {\n"
"        initlexer(&amp;lexer, &amp;error, fileaccess, fp);\n"
"    }\n"
"    else if (encoding == FMT_UTF16BE)\n"
"    {\n"
"        utf16buf.fp  = fp;\n"
"        initlexer(&amp;lexer, &amp;error, utf16accessbe, &amp;utf16buf);\n"
"    }\n"
"     else if (encoding == FMT_UTF16LE)\n"
"     {\n"
"         utf16buf.fp = fp;\n"
"         initlexer(&amp;lexer, &amp;error, utf16accessle, &amp;utf16buf);\n"
"     }\n"
"     else\n"
"     {\n"
"         snprintf(errormessage, Nerr, &quot;Can&apos;t determine text format of stream&quot;);\n"
"         return 0;\n"
"     }\n"
"\n"
"    answer = xmldocument(&amp;lexer, &amp;error);\n"
"    if (error.set)\n"
"    {\n"
"       snprintf(errormessage, Nerr, &quot;%s&quot;, error.message);\n"
"    }\n"
"       \n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
" Get the text encoding, gobbling the the first &apos;&lt;&apos;.\n"
" */\n"
"static int textencoding(FILE *fp)\n"
"{\n"
"    long pos;\n"
"    int ch1, ch2;\n"
"    int answer = 0;\n"
"    \n"
"    ch1 = fgetc(fp);\n"
"    ch2 = fgetc(fp);\n"
"    \n"
"    if (ch1 == &apos;&lt;&apos; &amp;&amp; ch2 != 0)\n"
"    {\n"
"        ungetc(ch2, fp);\n"
"        return FMT_UTF8;\n"
"    }\n"
"    else if (ch1 == &apos;&lt;&apos; &amp;&amp; ch2 == 0)\n"
"        return FMT_UTF16LE;\n"
"    else if (ch1 == 0 &amp;&amp; ch2 == &apos;&lt;&apos;)\n"
"        return FMT_UTF16BE;\n"
"    else if (ch1 == 0xFF &amp;&amp; ch2 == 0xFE)\n"
"    {\n"
"        ch1 = fgetc(fp);\n"
"        ch2 = fgetc(fp);\n"
"        while (ch1 + ch2 * 256 &lt; 128 &amp;&amp; isspace(ch1))\n"
"        {\n"
"            ch1 = fgetc(fp);\n"
"            ch2 = fgetc(fp);\n"
"        }\n"
"        if (ch1 == &apos;&lt;&apos; &amp;&amp; ch2 == &apos;0&apos;)\n"
"            return FMT_UTF16LE;\n"
"        else\n"
"            return FMT_UNKNOWN;\n"
"    }\n"
"    else if (ch1 == 0xFE &amp;&amp; ch2 == 0xFF)\n"
"    {\n"
"        ch1 = fgetc(fp);\n"
"        ch2 = fgetc(fp);\n"
"        while (ch1 * 256 + ch2 &lt; 128 &amp;&amp; isspace(ch2))\n"
"        {\n"
"            ch1 = fgetc(fp);\n"
"            ch2 = fgetc(fp);\n"
"        }\n"
"        if (ch1 == 0 &amp;&amp; ch2 == &apos;&lt;&apos;)\n"
"            return FMT_UTF16BE;\n"
"        else\n"
"            return FMT_UNKNOWN;\n"
"    }\n"
"    else if (ch1 == 0xEF &amp;&amp; ch2 == 0xBB)\n"
"    {\n"
"        ch1 = fgetc(fp);\n"
"        ch2 = fgetc(fp);\n"
"        if (ch1 == 0xBF)\n"
"        {\n"
"            while (isspace(ch2))\n"
"                ch2 = fgetc(fp);\n"
"            if (ch2 == &apos;&lt;&apos;)\n"
"                return FMT_UTF8;\n"
"            else\n"
"                return FMT_UNKNOWN;\n"
"        }\n"
"        else\n"
"            return FMT_UNKNOWN;\n"
"    }\n"
"    else if (isspace(ch1) &amp;&amp; ch2 == 0)\n"
"    {\n"
"        while (ch1 + ch2 * 256 &lt; 128 &amp;&amp; isspace(ch1))\n"
"        {\n"
"            ch1 = fgetc(fp);\n"
"            ch2 = fgetc(fp);\n"
"        }\n"
"        if (ch1 == &apos;&lt;&apos; &amp;&amp; ch2 == 0)\n"
"            return FMT_UTF16LE;\n"
"        else\n"
"            return FMT_UNKNOWN;\n"
"    }\n"
"    else if (ch1 == 0 &amp;&amp; isspace(ch2))\n"
"    {\n"
"        while (ch1 * 256 + ch2 &lt; 128 &amp;&amp; isspace(ch2))\n"
"        {\n"
"            ch1 = fgetc(fp);\n"
"            ch2 = fgetc(fp);\n"
"        }\n"
"        if (ch1 == 0 &amp;&amp; ch2 == &apos;&lt;&apos;)\n"
"            return FMT_UTF16BE;\n"
"        else\n"
"            return FMT_UNKNOWN;\n"
"    }\n"
"    else if (ch1 != 0 &amp;&amp; ch2 != 0)\n"
"    {\n"
"        while (isspace(ch1))\n"
"        {\n"
"            ch1 = ch2;\n"
"            ch2 = fgetc(fp);\n"
"        }\n"
"        if (ch1 == &apos;&lt;&apos;)\n"
"        {\n"
"            ungetc(ch2, fp);\n"
"            return FMT_UTF8;\n"
"        }\n"
"        else\n"
"            return FMT_UNKNOWN;\n"
"    }\n"
"    return FMT_UNKNOWN;\n"
"}\n"
"\n"
"static int fileaccess(void *ptr)\n"
"{\n"
"   FILE *fp = ptr;\n"
"   return fgetc(fp);\n"
"}\n"
"\n"
"static int utf16accessbe(void *ptr)\n"
"{\n"
"    struct utf16buff *up = ptr;\n"
"    int Nchars;\n"
"    int wch;\n"
"    int ch;\n"
"    \n"
"    if (up-&gt;rack[up-&gt;pos])\n"
"        return up-&gt;rack[up-&gt;pos++];\n"
"    else {\n"
"        wch = fgetc(up-&gt;fp);\n"
"        if (wch == EOF)\n"
"            return EOF;\n"
"        wch *= 256;\n"
"        ch = fgetc(up-&gt;fp);\n"
"        if (ch == EOF)\n"
"            return EOF;\n"
"        wch |= ch;\n"
"        Nchars = bbx_utf8_putch(up-&gt;rack, wch);\n"
"        up-&gt;rack[Nchars] = 0;\n"
"        up-&gt;pos = 0;\n"
"        return up-&gt;rack[up-&gt;pos++];\n"
"    }\n"
"    \n"
"}\n"
"\n"
"static int utf16accessle(void *ptr)\n"
"{\n"
"    struct utf16buff *up = ptr;\n"
"    int Nchars;\n"
"    int wch;\n"
"    int ch;\n"
"    \n"
"    if (up-&gt;rack[up-&gt;pos])\n"
"        return up-&gt;rack[up-&gt;pos++];\n"
"    else {\n"
"        wch = fgetc(up-&gt;fp);\n"
"        if (wch == EOF)\n"
"            return EOF;\n"
"        ch = fgetc(up-&gt;fp);\n"
"        if (ch == EOF)\n"
"            return EOF;\n"
"        wch |= (ch * 256);\n"
"        Nchars = bbx_utf8_putch(up-&gt;rack, wch);\n"
"        up-&gt;rack[Nchars] = 0;\n"
"        up-&gt;pos = 0;\n"
"        return up-&gt;rack[up-&gt;pos++];\n"
"    }\n"
"    \n"
"}\n"
"\n"
"static int bbx_utf8_putch(char *out, int ch)\n"
"{\n"
"  char *dest = out;\n"
"  if (ch &lt; 0x80)\n"
"  {\n"
"     *dest++ = (char)ch;\n"
"  }\n"
"  else if (ch &lt; 0x800)\n"
"  {\n"
"    *dest++ = (ch&gt;&gt;6) | 0xC0;\n"
"    *dest++ = (ch &amp; 0x3F) | 0x80;\n"
"  }\n"
"  else if (ch &lt; 0x10000)\n"
"  {\n"
"     *dest++ = (ch&gt;&gt;12) | 0xE0;\n"
"     *dest++ = ((ch&gt;&gt;6) &amp; 0x3F) | 0x80;\n"
"     *dest++ = (ch &amp; 0x3F) | 0x80;\n"
"  }\n"
"  else if (ch &lt; 0x110000)\n"
"  {\n"
"     *dest++ = (ch&gt;&gt;18) | 0xF0;\n"
"     *dest++ = ((ch&gt;&gt;12) &amp; 0x3F) | 0x80;\n"
"     *dest++ = ((ch&gt;&gt;6) &amp; 0x3F) | 0x80;\n"
"     *dest++ = (ch &amp; 0x3F) | 0x80;\n"
"  }\n"
"  else\n"
"    return 0;\n"
"  return dest - out;\n"
"}\n"
"\n"
"\n"
"\n"
"XMLDOC *xmldocfromstring(const char *str,char *errormessage, int Nerr)\n"
"{\n"
"   ERROR error;\n"
"   LEXER lexer;\n"
"   XMLDOC *answer = 0;\n"
"   struct strbuff strbuf;\n"
"    int ch;\n"
"    \n"
"    initerror(&amp;error);\n"
"\n"
"   if (errormessage &amp;&amp; Nerr &gt; 0)\n"
"      errormessage[0] = 0;\n"
"\n"
"    strbuf.str = str;\n"
"    strbuf.pos = 0;\n"
"    /* the lexer has been hacked to support non-seekable streams, so\n"
"     we need to pull out the first character */\n"
"    ch = stringaccess(&amp;strbuf);\n"
"    if (ch != &apos;&lt;&apos;)\n"
"    {\n"
"        snprintf(errormessage, Nerr, &quot;string must start with a \\&apos;&lt;\\&apos; character&quo"
"t;);\n"
"        return 0;\n"
"    }\n"
"    initlexer(&amp;lexer, &amp;error, stringaccess, &amp;strbuf);\n"
"    answer = xmldocument(&amp;lexer, &amp;error);\n"
"    if (error.set)\n"
"    {\n"
"         snprintf(errormessage, Nerr, &quot;%s&quot;, error.message);\n"
"    }\n"
"    return answer;\n"
"}\n"
"\n"
"static int stringaccess(void *ptr)\n"
"{\n"
"    struct strbuff *s = ptr;\n"
"    if (s-&gt;str[s-&gt;pos])\n"
"        return s-&gt;str[s-&gt;pos++];\n"
"    else\n"
"        return EOF;\n"
"}\n"
"\n"
"\n"
"/*\n"
"  document destructor\n"
"*/\n"
"void killxmldoc(XMLDOC *doc)\n"
"{\n"
"  if(doc)\n"
"  {\n"
"      killxmlnode(doc-&gt;root);\n"
"      free(doc);\n"
"  }\n"
"}\n"
"\n"
"/*\n"
"  get the root node of the document\n"
"*/\n"
"XMLNODE *xml_getroot(XMLDOC *doc)\n"
"{\n"
"  return doc-&gt;root;\n"
"}\n"
"\n"
"/*\n"
"  get a node&apos;s tag\n"
"*/\n"
"const char *xml_gettag(XMLNODE *node)\n"
"{\n"
"    return node-&gt;tag;\n"
"}\n"
"\n"
"/*\n"
"  get a node&apos;s data\n"
"*/\n"
"const char *xml_getdata(XMLNODE *node)\n"
"{\n"
"    return node-&gt;data;\n"
"}\n"
"\n"
"/*\n"
"  get a node&apos;s attributes\n"
"*/\n"
"const char *xml_getattribute(XMLNODE *node, const char *attr)\n"
"{\n"
"  XMLATTRIBUTE *next;\n"
"\n"
"  for(next = node-&gt;attributes; next; next = next-&gt;next)\n"
"    if(!strcmp(next-&gt;name, attr))\n"
"        return next-&gt;value;\n"
"\n"
"  return 0;\n"
"}\n"
"\n"
"/*\n"
"  get the number of direct children of the node\n"
"*/\n"
"int xml_Nchildren(XMLNODE *node)\n"
"{\n"
"  XMLNODE *next;\n"
"  int answer = 0;\n"
"\n"
"  if(node-&gt;child)\n"
"  {\n"
"    next = node-&gt;child;\n"
"    while(next)\n"
"    {\n"
"      answer++;\n"
"      next = next-&gt;next;\n"
"    }\n"
"  }\n"
"\n"
"  return answer;\n"
"}\n"
"\n"
"/*\n"
"  get the number of direct children with a particular tag\n"
"  Params: node - the node\n"
"          tag - the tag (NULL for all children)\n"
"  Returns: numer of children with that tag\n"
"*/\n"
"int xml_Nchildrenwithtag(XMLNODE *node, const char *tag)\n"
"{\n"
"  XMLNODE *next;\n"
"  int answer = 0;\n"
"\n"
"  if(node-&gt;child)\n"
"  {\n"
"    next = node-&gt;child;\n"
"    while(next)\n"
"    {\n"
"      if(tag == 0 || (next-&gt;tag &amp;&amp; !strcmp(next-&gt;tag, tag)))\n"
"        answer++;\n"
"      next = next-&gt;next;\n"
"    }\n"
"  }\n"
"\n"
"  return answer;\n"
"}\n"
"\n"
"/*\n"
"  get child with tag and index\n"
"  Params: node - the node\n"
"          tag - tag of child (NULL for all children)\n"
"          index - index number of child to retrieve\n"
"  Returns: child, or null on fail\n"
"  Notes: slow, only call for nodes with relatively small\n"
"    numbers of children. If the child list is very long,\n"
"    step through the linked list manually.\n"
"*/\n"
"XMLNODE *xml_getchild(XMLNODE *node, const char *tag, int index)\n"
"{\n"
"  XMLNODE *next;\n"
"  int count = 0;\n"
"\n"
"  if(node-&gt;child)\n"
"  {\n"
"    next = node-&gt;child;\n"
"    while(next)\n"
"    {\n"
"      if(tag == 0 || (next-&gt;tag &amp;&amp; !strcmp(next-&gt;tag, tag)))\n"
"      {\n"
"        if(count == index)\n"
"          return next;\n"
"        count++;\n"
"      }\n"
"      next = next-&gt;next;\n"
"    }\n"
"  }\n"
"\n"
"  return 0;\n"
"}\n"
"\n"
"/*\n"
"  recursive get descendants\n"
"  Params; node the the node\n"
"          tag - tag to retrieve\n"
"          list = pointer to return list of pointers to matchign nodes\n"
"          N - return for number of nodes found, also index of current place to write\n"
"  Returns: 0 on success -1 on out of memory\n"
"  Notes:\n"
"    we are descending the tree, allocating space for a pointer for every\n"
"    matching node.\n"
"\n"
"*/\n"
"static int getdescendants_r(XMLNODE *node, const char *tag,  XMLNODE ***list, int *N)\n"
"{\n"
"  XMLNODE **temp;\n"
"  XMLNODE *next;\n"
"  int err;\n"
"\n"
"  next = node;\n"
"  while(next)\n"
"  {\n"
"    if(tag == 0 || (next-&gt;tag &amp;&amp; !strcmp(next-&gt;tag, tag)))\n"
"    {\n"
"      temp = realloc(*list, (*N +1) * sizeof(XMLNODE *));\n"
"      if(!temp)\n"
"        return -1;\n"
"      *list = temp;\n"
"      (*list)[*N] = next;\n"
"      (*N)++;\n"
"    }\n"
"    if(next-&gt;child)\n"
"    {\n"
"      err = getdescendants_r(next-&gt;child, tag, list, N);\n"
"      if(err)\n"
"        return err;\n"
"    }\n"
"    next = next-&gt;next;\n"
"  }\n"
"\n"
"  return 0;\n"
"}\n"
"\n"
"/*\n"
" get all descendants that match a particular tag\n"
"   Params: node - the root node\n"
"           tag - the tag\n"
"           N - return for number found\n"
"   Returns: 0 on success, -1 on out of memory\n"
"   Notes: useful for fishing. Save you are reading the crossword\n"
"     tag, but you don&apos;t know exactly whther it is root or\n"
"     some child element. You also don&apos;t know if several of them are\n"
"     in the file. Just call to extract a list, then query for\n"
"     children so you know that the tag is an actual match.\n"
"     Don&apos;t call for huge lists as inefficient.\n"
"*/\n"
"XMLNODE **xml_getdescendants(XMLNODE *node, const char *tag, int *N)\n"
"{\n"
"  XMLNODE **answer = 0;\n"
"  int err;\n"
"\n"
"  *N = 0;\n"
"  err = getdescendants_r(node, tag, &amp;answer, N);\n"
"  if(err)\n"
"  {\n"
"    free(answer);\n"
"    return 0;\n"
"  }\n"
"\n"
"  return answer;\n"
"}\n"
"\n"
"static void getnestedata_r(XMLNODE *node, STRING *str, ERROR *err)\n"
"{\n"
"    XMLNODE *child;\n"
"    int i = 0;\n"
"    \n"
"    for (child = node-&gt;child; child; child = child-&gt;next)\n"
"    {\n"
"        while (node-&gt;data &amp;&amp; i &lt; child-&gt;position &amp;&amp; node-&gt;data[i])\n"
"            string_push(str, node-&gt;data[i++], err);\n"
"        getnestedata_r(child, str, err);\n"
"    }\n"
"    while (node-&gt;data &amp;&amp; node-&gt;data[i])\n"
"        string_push(str, node-&gt;data[i++], err);\n"
"    \n"
"}\n"
"\n"
"char *xml_getnesteddata(XMLNODE *node)\n"
"{\n"
"    ERROR error;\n"
"    STRING str;\n"
"    initerror(&amp;error);\n"
"    string_init(&amp;str);\n"
"    getnestedata_r(node, &amp;str, &amp;error);\n"
"    return string_release(&amp;str);\n"
"}\n"
"\n"
"/*\n"
"    get a node&apos;s line number.\n"
"    (note this one is null-guarded because it is meant to be called in error conditions)\n"
" */\n"
"int xml_getlineno(XMLNODE *node)\n"
"{\n"
"    if (node)\n"
"        return node-&gt;lineno;\n"
"    return -1;\n"
"}\n"
"/*\n"
"  Report any attributes which are not on a list of knowwn attributes\n"
" \n"
"  Params: node the node to test\n"
"        ... NULL-terminated list of known node names.\n"
"   Returns: a list of unrecognised attribures as a deep copy, 0 if\n"
"       there are none.\n"
"    Notes:\n"
"       Call as\n"
"       XMLATTRIBUTE *badattr =  0;\n"
"       char *end = 0;\n"
" \n"
"     badatttr = xml_unknownattributes(node, &quot;faith&quot;, &quot;hope&quot;, &quot;charity&quot;"
", end);\n"
"     if (badattr)\n"
"         // We&apos;ve got a node with some attributes we don&apos;t recognise\n"
"    \n"
"    Don&apos;t pass NULL for the terminator, becuase sometimes it expands to integer 0 and gets put "
"on the argument list as 32 bits instead of 64.\n"
" */\n"
"XMLATTRIBUTE *xml_unknownattributes(XMLNODE *node, ...)\n"
"{\n"
"    int *matches;\n"
"    va_list args;\n"
"    char *name;\n"
"    XMLATTRIBUTE *attr;\n"
"    XMLATTRIBUTE *copy = 0;\n"
"    XMLATTRIBUTE *answer = 0;\n"
"    int Nattributes = 0;\n"
"    int i;\n"
"    \n"
"    for (attr = node-&gt;attributes; attr != NULL; attr = attr-&gt;next)\n"
"        Nattributes++;\n"
"    \n"
"    if (Nattributes == 0)\n"
"        return 0;\n"
"    \n"
"    matches = malloc(Nattributes * sizeof(int));\n"
"    if (!matches)\n"
"        return 0;\n"
"    \n"
"    for (i = 0; i &lt; Nattributes; i++)\n"
"        matches[i] = 0;\n"
"    \n"
"    va_start(args, node);\n"
"    name = va_arg(args, char *);\n"
"\n"
"    while (name)\n"
"    {\n"
"       \n"
"        i = 0;\n"
"        for (attr = node-&gt;attributes; attr != NULL; attr = attr-&gt;next)\n"
"        {\n"
"            if (!strcmp(attr-&gt;name, name))\n"
"                matches[i]++;\n"
"            i++;\n"
"        }\n"
"        name = va_arg(args, char *);\n"
"    }\n"
"    va_end(args);\n"
"    \n"
"    i = 0;\n"
"    for (attr = node-&gt;attributes; attr != NULL; attr = attr-&gt;next)\n"
"    {\n"
"        if (!matches[i])\n"
"        {\n"
"            copy = malloc(sizeof(XMLATTRIBUTE));\n"
"            if (!copy)\n"
"                goto out_of_memory;\n"
"            copy-&gt;name = mystrdup(attr-&gt;name);\n"
"            if (!copy-&gt;name)\n"
"                goto out_of_memory;\n"
"            copy-&gt;value = mystrdup(attr-&gt;value);\n"
"            if (!copy-&gt;value)\n"
"                goto out_of_memory;\n"
"            copy-&gt;next = answer;\n"
"            answer = copy;\n"
"            copy = 0;\n"
"        }\n"
"        i++;\n"
"    }\n"
"    \n"
"    copy = 0;\n"
"    while (answer)\n"
"    {\n"
"        attr = answer;\n"
"        answer = answer-&gt;next;\n"
"        attr-&gt;next = copy;\n"
"        copy = attr;\n"
"    }\n"
"     answer = copy;\n"
"    \n"
"    free(matches);\n"
"    \n"
"    return answer;\n"
"    \n"
"out_of_memory:\n"
"    free(matches);\n"
"    if (copy)\n"
"    {\n"
"        free(copy-&gt;name);\n"
"        free(copy-&gt;value);\n"
"        free(copy);\n"
"    }\n"
"    killxmlattribute(answer);\n"
"    \n"
"    return  0;\n"
"}\n"
"\n"
"\n"
"/*\n"
"  xml node destructor\n"
"  Notes: destroy siblings in a list, chilren recursively\n"
"    as children are unlikely to be nested very deep\n"
"\n"
"*/\n"
"void killxmlnode(XMLNODE *node)\n"
"{\n"
"  XMLNODE *next;\n"
"\n"
"  if(node)\n"
"  {\n"
"    while(node)\n"
"    {\n"
"      next = node-&gt;next;\n"
"      if(node-&gt;child)\n"
"        killxmlnode(node-&gt;child);\n"
"      killxmlattribute(node-&gt;attributes);\n"
"      free(node-&gt;data);\n"
"      free(node-&gt;tag);\n"
"      free(node);\n"
"      node = next;\n"
"    }\n"
"  }\n"
"}\n"
"\n"
"/*\n"
"  destroy the attributes list\n"
"*/\n"
"static void killxmlattribute(XMLATTRIBUTE *attr)\n"
"{\n"
"  XMLATTRIBUTE *next;\n"
"  if(attr)\n"
"  {\n"
"    while(attr)\n"
"    {\n"
"       next = attr-&gt;next;\n"
"       free(attr-&gt;name);\n"
"       free(attr-&gt;value);\n"
"       free(attr);\n"
"       attr = next;\n"
"    }\n"
"  }\n"
"}\n"
"\n"
"static int is_initidentifier(int ch)\n"
"{\n"
"   if (isalpha(ch) || ch == &apos;_&apos;)\n"
"     return 1;\n"
"   return 0;\n"
"}\n"
"\n"
"static int is_elementnamech(int ch)\n"
"{\n"
"   if (isalpha(ch) || isdigit(ch) || ch == &apos;_&apos; || ch == &apos;-&apos; || ch == &apos;.&apo"
"s; || ch == &apos;:&apos;)\n"
"     return 1;\n"
"   return 0;\n"
"}\n"
"\n"
"static int is_attributenamech(int ch)\n"
"{\n"
"   if (isalpha(ch) || isdigit(ch) || ch == &apos;_&apos; || ch == &apos;-&apos; || ch == &apos;.&apo"
"s; || ch == &apos;:&apos;)\n"
"     return 1;\n"
"   return 0;\n"
"}\n"
"\n"
"static void trim(char *str)\n"
"{\n"
"    int i;\n"
"    \n"
"    for (i = 0; str[i]; i++)\n"
"        if (!isspace((unsigned char) str[i]))\n"
"            break;\n"
"    if (i != 0)\n"
"        memmove(str, &amp;str[i], strlen(str) -i + 1);\n"
"    if (str[0])\n"
"    {\n"
"        i = strlen(str) -1;\n"
"        while (i &gt;= 0 &amp;&amp; isspace((unsigned char)str[i]))\n"
"           str[i--] = 0;\n"
"    }\n"
"        \n"
"}\n"
"\n"
"static int string_init(STRING *s)\n"
"{\n"
"  s-&gt;str = 0;\n"
"  s-&gt;capacity = 0;\n"
"  s-&gt;N = 0;\n"
"    \n"
"  return 0;\n"
"}\n"
"\n"
"static void string_push(STRING *s, int ch, ERROR *err)\n"
"{\n"
"    char *temp = 0;\n"
"    \n"
"   if (s-&gt;capacity &lt; s-&gt;N * 2 + 2)\n"
"   {\n"
"     temp = realloc(s-&gt;str, s-&gt;N * 2 + 2);\n"
"     if (!temp)\n"
"       goto out_of_memory;\n"
"     s-&gt;str = temp;\n"
"     s-&gt;capacity = s-&gt;N * 2 + 2; \n"
"   }\n"
"   s-&gt;str[s-&gt;N++] = ch;\n"
"   s-&gt;str[s-&gt;N] = 0;\n"
"   return;\n"
"out_of_memory:\n"
"   reporterror(err, &quot;out of memory&quot;);\n"
"\n"
"}\n"
"\n"
"static void string_concat(STRING *s, const char *str, ERROR *err)\n"
"{\n"
"    int i;\n"
"    \n"
"    for (i =0; str[i]; i++)\n"
"        string_push(s, str[i], err);\n"
"}\n"
"\n"
"static char *string_release(STRING *s)\n"
"{\n"
"   char *answer;\n"
"\n"
"   if (s-&gt;str == 0)\n"
"   {\n"
"       s-&gt;str = malloc(1);\n"
"       if (s-&gt;str)\n"
"           s-&gt;str[0] = 0;\n"
"       else\n"
"           return 0;\n"
"       \n"
"   }\n"
"   answer = realloc(s-&gt;str, s-&gt;N + 1);\n"
"   s-&gt;str = 0;\n"
"   s-&gt;N = 0;\n"
"   s-&gt;capacity = 0;\n"
"\n"
"   return answer;\n"
"}\n"
"\n"
"static XMLDOC *xmldocument(LEXER *lex, ERROR *err)\n"
"{\n"
"    XMLNODE *node;\n"
"    XMLDOC *doc;\n"
"    int ch;\n"
"    int shriek;\n"
"    \n"
"    doc = malloc(sizeof(XMLDOC));\n"
"    if (!doc)\n"
"    {\n"
"        reporterror(err, &quot;out of memory&quot;);\n"
"        return 0;\n"
"    }\n"
"    \n"
"    skipbom(lex, err);\n"
"\n"
"    do {\n"
"        skipwhitespace(lex, err);\n"
"        if (!match(lex, &apos;&lt;&apos;))\n"
"            reporterror(err, &quot;can&apos;t find opening tag&quot;);\n"
"        ch = gettoken(lex);\n"
"        if (is_initidentifier(ch))\n"
"        {\n"
"            node = xmlnode(lex, err);\n"
"            if (node)\n"
"            {\n"
"                if (!err-&gt;set)\n"
"                {\n"
"                    doc-&gt;root = node;\n"
"                    return doc;\n"
"                }\n"
"                else\n"
"                {\n"
"                    killxmlnode(node);\n"
"                    break;\n"
"                }\n"
"            }\n"
"            else\n"
"            {\n"
"                reporterror(err, &quot;bad root node&quot;);\n"
"            }\n"
"        }\n"
"        else if (ch == &apos;!&apos;)\n"
"        {\n"
"            shriek = shriektype(lex, err);\n"
"            if (shriek == COMMENT)\n"
"                comment(lex, err);\n"
"            else\n"
"                skipunknowntag(lex, err);\n"
"        }\n"
"        else  if (ch == &apos;\?&apos;)\n"
"        {\n"
"            char *text;\n"
"            text = processinginstruction(lex, err);\n"
"            free(text);\n"
"        }\n"
"        else {\n"
"            skipunknowntag(lex, err);\n"
"        }\n"
"    } while (ch != EOF);\n"
"    \n"
"    free(doc);\n"
"    return 0;\n"
"}\n"
"\n"
"static XMLNODE *xmlnode(LEXER *lex, ERROR *err)\n"
"{\n"
"    int ch;\n"
"    char *tag = 0;\n"
"    XMLATTRIBUTE *attributes = 0;\n"
"    XMLNODE *node = 0;\n"
"    XMLNODE *lastchild = 0;\n"
"    STRING datastr;\n"
"    int shriek;\n"
"    int lineno;\n"
"    \n"
"    if (err-&gt;set)\n"
"        return 0;\n"
"    enterrecursion(err);\n"
"    \n"
"    string_init(&amp;datastr);\n"
"    \n"
"    lineno = lex-&gt;lineno;\n"
"    tag = elementname(lex, err);\n"
"    if (!tag)\n"
"        goto parse_error;\n"
"    attributes = attributelist(lex, err);\n"
"    skipwhitespace(lex, err);\n"
"    ch = gettoken(lex);\n"
"    if (ch == &apos;/&apos;)\n"
"    {\n"
"        match(lex, &apos;/&apos;);\n"
"        if (!match(lex, &apos;&gt;&apos;))\n"
"            goto parse_error;\n"
"        node = malloc(sizeof(XMLNODE));\n"
"        if (!node)\n"
"            goto out_of_memory;\n"
"        node-&gt;tag = tag;\n"
"        node-&gt;attributes = attributes;\n"
"        node-&gt;data = 0;\n"
"        node-&gt;position = 0;\n"
"        node-&gt;lineno = lineno;\n"
"        node-&gt;child = 0;\n"
"        node-&gt;next = 0;\n"
"        endrecursion(err);\n"
"        return node;\n"
"    }\n"
"    else if (ch == &apos;&gt;&apos;)\n"
"    {\n"
"        match(lex, &apos;&gt;&apos;);\n"
"        node = malloc(sizeof(XMLNODE));\n"
"        if (!node)\n"
"            goto out_of_memory;\n"
"        node-&gt;tag = tag;\n"
"        node-&gt;attributes = attributes;\n"
"        node-&gt;data = 0;\n"
"        node-&gt;position = 0;\n"
"        node-&gt;lineno = lineno;\n"
"        node-&gt;child = 0;\n"
"        node-&gt;next = 0;\n"
"        tag = 0;\n"
"        attributes = 0;\n"
"        \n"
"        do {\n"
"            char *text = textspan(lex, err);\n"
"            if (text)\n"
"            {\n"
"                string_concat(&amp;datastr, text, err);\n"
"                free(text);\n"
"                text = 0;\n"
"            }\n"
"            ch = gettoken(lex);\n"
"            if (ch == &apos;&lt;&apos;)\n"
"            {\n"
"                match(lex, &apos;&lt;&apos;);\n"
"                ch = gettoken(lex);\n"
"                if (is_initidentifier(ch))\n"
"                {\n"
"                    XMLNODE *child = xmlnode(lex, err);\n"
"                    if (!child)\n"
"                        goto parse_error;\n"
"                    if (lastchild)\n"
"                        lastchild-&gt;next = child;\n"
"                    else\n"
"                        node-&gt;child = child;\n"
"                    lastchild = child;\n"
"                    child-&gt;position = datastr.N;\n"
"                }\n"
"                else if(ch == &apos;/&apos;)\n"
"                {\n"
"                    match(lex, &apos;/&apos;);\n"
"                    tag = elementname(lex,err);\n"
"                    if (tag &amp;&amp; !strcmp(tag, node-&gt;tag))\n"
"                    {\n"
"                        node-&gt;data = string_release(&amp;datastr);\n"
"                        free(tag);\n"
"                        match(lex, &apos;&gt;&apos;);\n"
"                        endrecursion(err);\n"
"                        return node;\n"
"                    }\n"
"                    else\n"
"                    {\n"
"                        reporterror(err, &quot;bad closing tag %s&quot;, tag);\n"
"                        goto parse_error;\n"
"                    }\n"
"                }\n"
"                else if (ch == &apos;!&apos;)\n"
"                {\n"
"                    shriek = shriektype(lex, err);\n"
"                    if (shriek == COMMENT)\n"
"                        comment(lex, err);\n"
"                    else if(shriek == CDATA)\n"
"                    {\n"
"                        text = cdata(lex, err);\n"
"                        if (text)\n"
"                            string_concat(&amp;datastr, text, err);\n"
"                        free(text);\n"
"                    }\n"
"                }\n"
"                else if (ch == &apos;\?&apos;)\n"
"                {\n"
"                    text = processinginstruction(lex, err);\n"
"                    free (text);\n"
"                }\n"
"                else\n"
"                {\n"
"                    goto parse_error;\n"
"                }\n"
"            }\n"
"        } while (ch != EOF);\n"
"    }\n"
"    else\n"
"    {\n"
"        goto parse_error;\n"
"    }\n"
"parse_error:\n"
"    reporterror(err, &quot;error parsing element&quot;);\n"
"    endrecursion(err);\n"
"    return 0;\n"
"out_of_memory:\n"
"    reporterror(err, &quot;out of memory&quot;);\n"
"    endrecursion(err);\n"
"    return 0;\n"
"}\n"
"\n"
"static XMLNODE *comment(LEXER *lex, ERROR *err)\n"
"{\n"
"    char buff[4] = {0};\n"
"    int ch;\n"
"    int lineno;\n"
"    \n"
"    lineno = lex-&gt;lineno;\n"
"    \n"
"    while ((ch = gettoken(lex)) != EOF)\n"
"    {\n"
"        match(lex, ch);\n"
"        memmove(buff, buff+1, 3);\n"
"        buff[2] = ch;\n"
"        if (!strcmp(buff, &quot;--&gt;&quot;))\n"
"            return 0;\n"
"    }\n"
"    \n"
"    reporterror(err, &quot;bad comment (starts line&quot;);\n"
"    return 0;\n"
"          \n"
"}\n"
"\n"
"static XMLATTRIBUTE *attributelist(LEXER *lex, ERROR *err)\n"
"{\n"
"    int ch;\n"
"    XMLATTRIBUTE *answer = 0;\n"
"    XMLATTRIBUTE *last = 0;\n"
"    XMLATTRIBUTE *attr = 0;\n"
"    \n"
"    while(1)\n"
"    {\n"
"        skipwhitespace(lex, err);\n"
"        ch = gettoken(lex);\n"
"        if (is_initidentifier(ch))\n"
"        {\n"
"            attr = xmlattribute(lex, err);\n"
"            if (!attr)\n"
"                goto parse_error;\n"
"            if (last)\n"
"            {\n"
"                last-&gt;next = attr;\n"
"                last = attr;\n"
"            }\n"
"            else\n"
"            {\n"
"                answer = attr;\n"
"                last = answer;\n"
"            }\n"
"        }\n"
"        else\n"
"        {\n"
"            break;\n"
"        }\n"
"    }\n"
"    \n"
"    return answer;\n"
"parse_error:\n"
"    killxmlattribute(answer);\n"
"    \n"
"    return 0;\n"
"}\n"
"\n"
"static XMLATTRIBUTE *xmlattribute(LEXER *lex, ERROR *err)\n"
"{\n"
"    char *name = 0;\n"
"    char *value = 0;\n"
"    XMLATTRIBUTE *answer = 0;\n"
"    \n"
"    name = attributename(lex, err);\n"
"    if (!name)\n"
"        goto parse_error;\n"
"    skipwhitespace(lex, err);\n"
"    if (!match(lex, &apos;=&apos;))\n"
"        goto parse_error;\n"
"    skipwhitespace(lex, err);\n"
"    value = quotedstring(lex, err);\n"
"    if (!value)\n"
"        goto parse_error;\n"
"    \n"
"    answer = malloc(sizeof(XMLATTRIBUTE));\n"
"    if (!answer)\n"
"        goto out_of_memory;\n"
"    answer-&gt;name = name;\n"
"    answer-&gt;value = value;\n"
"    answer-&gt;next = 0;\n"
"    return answer;\n"
"    \n"
"parse_error:\n"
"    reporterror(err, &quot;error in attribute&quot;);\n"
"    free(name);\n"
"    free(value);\n"
"    free(answer);\n"
"    return 0;\n"
"    \n"
"out_of_memory:\n"
"    reporterror(err, &quot;out of memory&quot;);\n"
"    free(name);\n"
"    free(value);\n"
"    free(answer);\n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"\n"
"static char *quotedstring(LEXER *lex, ERROR *err)\n"
"{\n"
"    int quotech;\n"
"    STRING str;\n"
"    int ch;\n"
"    \n"
"    string_init(&amp;str);\n"
"    \n"
"    quotech = gettoken(lex);\n"
"    if (quotech != &apos;\\&quot;&apos; &amp;&amp; quotech != &apos;\\&apos;&apos;)\n"
"    {\n"
"        goto parse_error;\n"
"    }\n"
"    match(lex, quotech);\n"
"    while ((ch = gettoken(lex)) != EOF)\n"
"    {\n"
"        if (ch == quotech)\n"
"            break;\n"
"        else if (ch == &apos;&amp;&apos;)\n"
"        {\n"
"            ch = escapechar(lex, err);\n"
"            string_push(&amp;str, ch, err);\n"
"        }\n"
"        else if (ch == &apos;\\n&apos;)\n"
"            goto parse_error;\n"
"        else\n"
"        {\n"
"            string_push(&amp;str,ch, err);\n"
"            match(lex, ch);\n"
"        }\n"
"    }\n"
"    if (!match(lex, quotech))\n"
"        goto parse_error;\n"
"    return string_release(&amp;str);\n"
"parse_error:\n"
"    free(string_release(&amp;str));\n"
"    reporterror(err, &quot;bad quoted string&quot;);\n"
"    return 0;\n"
"    \n"
"}\n"
"\n"
"static char *textspan(LEXER *lex, ERROR *err)\n"
"{\n"
"   int ch;\n"
"   STRING str;\n"
"\n"
"   string_init(&amp;str);\n"
"\n"
"   while ( (ch = gettoken(lex)) != EOF)\n"
"   {\n"
"      if (ch == &apos;&lt;&apos;)\n"
"          break;\n"
"      if (ch == &apos;&amp;&apos;)\n"
"        ch = escapechar(lex, err);\n"
"      else\n"
"         match(lex, ch);\n"
"      string_push(&amp;str, ch, err);\n"
"   } \n"
"   \n"
"    return string_release(&amp;str);\n"
"}\n"
"\n"
"static char *cdata(LEXER *lex, ERROR *err)\n"
"{\n"
"    char buff[4] = {0};\n"
"    int ch;\n"
"    int i;\n"
"    STRING str;\n"
"    int lineno;\n"
"    \n"
"    lineno = lex-&gt;lineno;\n"
"    \n"
"    string_init(&amp;str);\n"
"    \n"
"    match(lex, &apos;[&apos;);\n"
"    \n"
"    for (i =0; i &lt; 3; i++)\n"
"    {\n"
"        ch = gettoken(lex);\n"
"        buff[i] = ch;\n"
"        match(lex, ch);\n"
"    }\n"
"    \n"
"    while ((ch = gettoken(lex)) != EOF)\n"
"    {\n"
"        string_push(&amp;str, buff[0], err);\n"
"        buff[0] = buff[1];\n"
"        buff[1] = buff[2];\n"
"        buff[2] = ch;\n"
"        match(lex, ch);\n"
"        if (!strcmp(buff, &quot;]]&gt;&quot;))\n"
"            return string_release(&amp;str);\n"
"    }\n"
"    free (string_release(&amp;str));\n"
"    reporterror(err, &quot;unterminated CDATA tag (starts line %d)&quot;, lineno);\n"
"    \n"
"    return 0;\n"
"}\n"
"\n"
"static char *processinginstruction(LEXER *lex, ERROR *err)\n"
"{\n"
"    int ch;\n"
"    STRING str;\n"
"    int lineno;\n"
"    \n"
"    string_init(&amp;str);\n"
"    lineno = lex-&gt;lineno;\n"
"    \n"
"    match(lex, &apos;\?&apos;);\n"
"    \n"
"    while ((ch = gettoken(lex)) != EOF)\n"
"    {\n"
"        match(lex, ch);\n"
"        if (ch == &apos;\?&apos;)\n"
"        {\n"
"            if (gettoken(lex) == &apos;&gt;&apos;)\n"
"            {\n"
"                match(lex, &apos;&gt;&apos;);\n"
"                return string_release(&amp;str);\n"
"            }\n"
"        }\n"
"    }\n"
"    reporterror(err, &quot;&lt;\? tag not closed (starts line %d)&quot;, lineno);\n"
"    free (string_release(&amp;str));\n"
"    \n"
"    return 0;\n"
"}\n"
"\n"
"static char *attributename(LEXER *lex, ERROR *err)\n"
"{\n"
"    int ch;\n"
"    STRING str;\n"
"\n"
"    string_init(&amp;str);\n"
"    \n"
"    ch = gettoken(lex);\n"
"    if (!is_initidentifier(ch))\n"
"      goto parse_error;\n"
"    while (is_attributenamech(ch))\n"
"    {\n"
"       match(lex, ch);\n"
"       string_push(&amp;str, ch, err);\n"
"       ch = gettoken(lex);\n"
"    }\n"
"    return string_release(&amp;str);\n"
" parse_error:\n"
"    free(string_release(&amp;str));\n"
"    return 0;\n"
"}\n"
"\n"
"static char *elementname(LEXER *lex, ERROR *err)\n"
"{\n"
"   int ch;\n"
"   STRING str;\n"
"    char *temp = 0;\n"
"\n"
"   string_init(&amp;str);\n"
"   \n"
"   ch = gettoken(lex);\n"
"   if (!is_initidentifier(ch))\n"
"     goto parse_error;\n"
"   while (is_elementnamech(ch))\n"
"   {\n"
"      match(lex, ch);\n"
"      string_push(&amp;str, ch, err);\n"
"      ch = gettoken(lex);\n"
"   }\n"
"   return string_release(&amp;str);\n"
"parse_error:\n"
"    temp = string_release(&amp;str);\n"
"    free(temp);\n"
"   return 0;\n"
"}\n"
"\n"
"static int escapechar(LEXER *lex, ERROR *err)\n"
"{\n"
"    int ch;\n"
"    STRING str;\n"
"    char *escaped = 0;\n"
"    int answer = 0;\n"
"    \n"
"    string_init(&amp;str);\n"
"    \n"
"    ch = gettoken(lex);\n"
"    if (!match(lex, &apos;&amp;&apos;))\n"
"        goto parse_error;\n"
"    string_push(&amp;str, ch, err);\n"
"    while ( (ch = gettoken(lex)) != EOF)\n"
"    {\n"
"        string_push(&amp;str, ch, err);\n"
"        match(lex, ch);\n"
"        if (ch == &apos;;&apos;)\n"
"            break;\n"
"        if (ch == &apos;\\n&apos;)\n"
"            goto parse_error;\n"
"    }\n"
"    escaped = string_release(&amp;str);\n"
"    if (!strcmp(escaped, &quot;&amp;amp;&quot;))\n"
"        answer = &apos;&amp;&apos;;\n"
"    else if (!strcmp(escaped, &quot;&amp;gt;&quot;))\n"
"        answer = &apos;&gt;&apos;;\n"
"    else if (!strcmp(escaped, &quot;&amp;lt;&quot;))\n"
"        answer = &apos;&lt;&apos;;\n"
"    else if (!strcmp(escaped, &quot;&amp;quot;&quot;))\n"
"        answer = &apos;\\&quot;&apos;;\n"
"    else if (!strcmp(escaped, &quot;&amp;apos;&quot;))\n"
"        answer = &apos;\\&apos;&apos;;\n"
"    if (answer == 0)\n"
"        reporterror(err, &quot;Unrecognised escape sequence %s&quot;, escaped);\n"
"    \n"
"    free(escaped);\n"
"    return answer;\n"
"parse_error:\n"
"    free(escaped);\n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"/*\n"
"&lt;!-- begins a comment, which ends with --&gt;\n"
"\n"
"&lt;![CDATA[ begins a CDATA section, which ends with ]]&gt;\n"
"\n"
"&lt;![INCLUDE[ and &lt;![IGNORE[ begin conditional sections, which end with ]]&gt;.\n"
"\n"
"&lt;!DOCTYPE begins a document type declaration.\n"
"\n"
"&lt;!ELEMENT begins an element type declaration.\n"
"\n"
"&lt;!ATTLIST begins an attribute list declaration.\n"
"\n"
"&lt;!ENTITY begins an entity declaration.\n"
"\n"
"&lt;!NOTATION begins a notation declaration.\n"
"*/\n"
"\n"
"static int shriektype(LEXER *lex, ERROR *err)\n"
"{\n"
"    char buff[32] = {0};\n"
"    int ch;\n"
"    int i  = 0;\n"
"    \n"
"    match(lex, &apos;!&apos;);\n"
"    buff[i++] = &apos;!&apos;;\n"
"    \n"
"    while ((ch = gettoken(lex)) != EOF)\n"
"    {\n"
"        if (ch == &apos;&lt;&apos; || ch == &apos;&gt;&apos;)\n"
"            return UNKNOWNSHRIEK;\n"
"        if (isspace(ch))\n"
"            return UNKNOWNSHRIEK;\n"
"        buff[i++] = ch;\n"
"        if (i &gt;= sizeof(buff))\n"
"            break;\n"
"        if (!strcmp(buff, &quot;!--&quot;))\n"
"        {\n"
"            match(lex, ch);\n"
"            return COMMENT;\n"
"        }\n"
"        if (!strcmp(buff, &quot;![CDATA[&quot;))\n"
"            return  CDATA;\n"
"        if (!strcmp(buff, &quot;![INCLUDE[&quot;))\n"
"            return INCLUDE;\n"
"        if (!strcmp(buff, &quot;![IGNORE[&quot;))\n"
"            return IGNORE;\n"
"        if (!strcmp(buff, &quot;!DOCTYPE&quot;))\n"
"        {\n"
"            match(lex, ch);\n"
"            return DOCTYPE;\n"
"        }\n"
"        if (!strcmp(buff, &quot;!ELEMENT&quot;))\n"
"        {\n"
"            match(lex, ch);\n"
"            return ELEMENT;\n"
"        }\n"
"        if (!strcmp(buff, &quot;!ATTLIST&quot;))\n"
"        {\n"
"            match(lex, ch);\n"
"            return ATTLIST;\n"
"        }\n"
"        if (!strcmp(buff, &quot;NOTATION&quot;))\n"
"        {\n"
"            match(lex, ch);\n"
"            return NOTATION;\n"
"        }\n"
"        match(lex, ch);\n"
"    }\n"
"    \n"
"    return UNKNOWNSHRIEK;\n"
"}\n"
"\n"
"static void skipbom(LEXER *lex, ERROR *err)\n"
"{\n"
"    int ch;\n"
"    ch = gettoken(lex);\n"
"    if (ch == 0xEF)\n"
"    {\n"
"        match(lex, ch);\n"
"        if (!match(lex, 0xBB))\n"
"            reporterror(err, &quot;input has bad byte order marker&quot;);\n"
"        if (!match(lex, 0xBF))\n"
"            reporterror(err, &quot;input has bad byte order marker&quot;);\n"
"    }\n"
"}\n"
"               \n"
"static void skipunknowntag(LEXER *lex, ERROR *err)\n"
"{\n"
"    int ch;\n"
"    \n"
"    enterrecursion(err);\n"
"    while ((ch = gettoken(lex)) != EOF)\n"
"    {\n"
"        match(lex, ch);\n"
"        if (ch == &apos;&lt;&apos;)\n"
"            skipunknowntag(lex, err);\n"
"        if (ch == &apos;&gt;&apos;)\n"
"            break;\n"
"    }\n"
"    endrecursion(err);\n"
"}\n"
"\n"
"static void skipwhitespace(LEXER *lex, ERROR *err)\n"
"{\n"
"   int ch = gettoken(lex);\n"
"   while (isspace(ch))\n"
"   {\n"
"     match(lex, ch);\n"
"     ch = gettoken(lex);\n"
"   }\n"
"}\n"
"static void initerror(ERROR *err)\n"
"{\n"
"    err-&gt;set = 0;\n"
"    err-&gt;message[0] = 0;\n"
"    err-&gt;lexer = 0;\n"
"    err-&gt;recursiondepth = 0;\n"
"}\n"
"\n"
"static void enterrecursion(ERROR *err)\n"
"{\n"
"    err-&gt;recursiondepth++;\n"
"    if (err-&gt;recursiondepth &gt; MAXRECURSIONLIMIT)\n"
"    {\n"
"        reporterror(err, &quot;nesting too deep&quot;);\n"
"        if (err-&gt;lexer)\n"
"            match(err-&gt;lexer, EOF);\n"
"    }\n"
"}\n"
"\n"
"static void endrecursion(ERROR *err)\n"
"{\n"
"    err-&gt;recursiondepth--;\n"
"    if (err-&gt;recursiondepth &lt; 0)\n"
"        reporterror(err, &quot;nesting problem&quot;);\n"
"}\n"
"           \n"
"static void reporterror(ERROR *err, const char *fmt, ...)\n"
"{\n"
"    char buff[1024];\n"
"    va_list args;\n"
"    \n"
"    if (!err-&gt;set)\n"
"    {\n"
"        va_start(args, fmt);\n"
"        vsnprintf(buff, 1024, fmt, args);\n"
"        va_end(args);\n"
"        if (err-&gt;lexer)\n"
"            snprintf(err-&gt;message, 1024, &quot;Error line %d: %s.&quot;, err-&gt;lexer-&gt;lineno"
", buff);\n"
"        else\n"
"            strcpy(err-&gt;message, buff);\n"
"        err-&gt;set = 1;\n"
"    }\n"
"}\n"
"\n"
"static void initlexer(LEXER *lex, ERROR *err, int (*getch)(void *), void *ptr)\n"
"{\n"
"  lex-&gt;getch = getch;\n"
"  lex-&gt;ptr = ptr;\n"
"  lex-&gt;err = err;\n"
"  lex-&gt;lineno = 0;\n"
"  lex-&gt;columnno = 0;\n"
"  lex-&gt;badmatch = 0;\n"
"  err-&gt;lexer = lex;\n"
"  /* hacked. Put a &apos;&lt;&apos; sitting in the token becuase non-seekable UTF-16 streams\n"
"   need to read this character to determine data format */\n"
"    lex-&gt;token = &apos;&lt;&apos;; //(*lex-&gt;getch)(lex-&gt;ptr);\n"
"  if (lex-&gt;token != EOF)\n"
"  {\n"
"    lex-&gt;lineno = 1;\n"
"    lex-&gt;columnno = 1; \n"
"  }\n"
"  else\n"
"  {\n"
"     reporterror(lex-&gt;err, &quot;Can&apos;t read data\\n&quot;);\n"
"  }\n"
"\n"
"}\n"
"\n"
"static int gettoken(LEXER *lex)\n"
"{\n"
"    if (lex-&gt;badmatch)\n"
"        return EOF;\n"
"   return lex-&gt;token;\n"
"}\n"
"\n"
"static int match(LEXER *lex, int token)\n"
"{\n"
"   if (lex-&gt;token == token)\n"
"   {\n"
"       if (lex-&gt;token == &apos;\\n&apos;)\n"
"       {\n"
"          lex-&gt;lineno++;\n"
"          lex-&gt;columnno = 1;\n"
"       }\n"
"      else\n"
"      {\n"
"        lex-&gt;columnno++;\n"
"      }\n"
"       lex-&gt;token = (*lex-&gt;getch)(lex-&gt;ptr);\n"
"      return 1;\n"
"   }\n"
"   else\n"
"   {\n"
"       lex-&gt;badmatch = 1;\n"
"     return 0;\n"
"   }\n"
"}\n"
"\n"
"static char *mystrdup(const char *str)\n"
"{\n"
"  char *answer;\n"
"\n"
"  answer = malloc(strlen(str) + 1);\n"
"  if(answer)\n"
"    strcpy(answer, str);\n"
"\n"
"  return answer;\n"
"}\n"
"\n"
"\n"
"static void printnode_r(XMLNODE *node, int depth)\n"
"{\n"
"    int i;\n"
"    XMLNODE *child;\n"
"    \n"
"    for (i =0; i &lt; depth; i++)\n"
"        printf(&quot;\\t&quot;);\n"
"    printf(&quot;&lt;%s&gt;\\n&quot;, node-&gt;tag);\n"
"    for (child = node-&gt;child; child; child = child-&gt;next)\n"
"        printnode_r(child, depth +1);\n"
"    for (i =0; i &lt; depth; i++)\n"
"        printf(&quot;\\t&quot;);\n"
"    printf(&quot;&lt;/%s&gt;\\n&quot;, node-&gt;tag);\n"
"}\n"
"\n"
"static void printdocument(XMLDOC *doc)\n"
"{\n"
"    if (!doc)\n"
"        printf(&quot;Document was null\\n&quot;);\n"
"    else if(!doc-&gt;root)\n"
"        printf(&quot;root null\\n&quot;);\n"
"    else\n"
"        printnode_r(doc-&gt;root, 0);\n"
"}\n"
"\n"
"int xmlparser2main(int argc, char **argv)\n"
"{\n"
"    XMLDOC *doc;\n"
"    \n"
"    char error[1024];\n"
"    if (argc == 1)\n"
"    {\n"
"        doc = xmldocfromstring(&quot;&lt;!-- --&gt;&lt;FRED attr=\\&quot;Fred\\&quot;&gt;Fred&lt;![CDA"
"TA[character &gt; data]]&gt;&lt;JIM/&gt;Bert&lt;JIM/&gt;Harry&lt;/FRED&gt;&quot;, error, 1204);\n"
"        //printf(&quot;%s\\n&quot;, doc-&gt;root-&gt;data);\n"
"    }else\n"
"    {\n"
"        doc = loadxmldoc(argv[1], error, 1024);\n"
"        if (doc)\n"
"            printf(&quot;%s\\n&quot;, xml_getnesteddata(doc-&gt;root));\n"
"    }\n"
"    if (error[0])\n"
"        printf(&quot;%s\\n&quot;, error);\n"
"    else\n"
"        printdocument(doc);\n"
"    \n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"bbx_write_source.c\" type=\"text\">\n"
"//\n"
"//  bbx_write_source.c\n"
"//  testbabyxfilesystem\n"
"//\n"
"//  Created by Malcolm McLean on 31/05/2024.\n"
"//\n"
"#include &lt;stdio.h&gt;\n"
"#include &lt;string.h&gt;\n"
"#include &lt;stdlib.h&gt;\n"
"#include &lt;ctype.h&gt;\n"
"\n"
"\n"
"#include &quot;bbx_write_source.h&quot;\n"
"#include &quot;asciitostring.h&quot;\n"
"#include &quot;xmlparser2.h&quot;\n"
"\n"
"/*\n"
"   Does a string cnsist entirely of white space\? (also treat nulls as white)\n"
" */\n"
"static int strwhitespace(const char *str)\n"
"{\n"
"    if (!str)\n"
"        return 1;\n"
"    while (*str)\n"
"    {\n"
"        if (!isspace((unsigned char) *str))\n"
"            return  0;\n"
"        str++;\n"
"    }\n"
"    \n"
"    return 1;\n"
"}\n"
"\n"
"/*\n"
" The functions\n"
" \n"
" unsigned char *uudecodestr(const char *uucode, int *N)\n"
" char *uuencodestr(const unsigned char *binary, int N)\n"
" \n"
" Have been modifed by Malcolm McLean from the functions\n"
" \n"
" void UUEncode( char * pSrcFilename, FILE * pSrcFile, FILE * pDstFile )\n"
" bool UUDecode( FILE * pSrcFile )\n"
" \n"
" by Tom Weatherhead.\n"
" \n"
" They appear in Tom&apos;s github project &quot;helix&quot;.\n"
" \n"
"The MIT License (MIT)\n"
"\n"
"Copyright (c) 2002-2021 Tom Weatherhead\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the &quot;Software&quot;), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
"SOFTWARE.\n"
" \n"
" */\n"
"\n"
"static unsigned char *uudecodestr(const char *uucode, int *N)\n"
"{\n"
"    int Nmaxout;\n"
"    unsigned char *out;\n"
"    int ix = 0;\n"
"    int jx = 0;\n"
"    int k;\n"
"    int i;\n"
"    \n"
"    Nmaxout = ((int)strlen(uucode)/60 +1) * 45;\n"
"    out = malloc(Nmaxout);\n"
"    \n"
"    while( uucode[ix] )\n"
"    {\n"
"        // Get a line of the src text file.\n"
"        char acSrcData[128];\n"
"        \n"
"        k = 0;\n"
"        while (uucode[ix] != &apos;\\n&apos; &amp;&amp; uucode[ix])\n"
"        {\n"
"            acSrcData[k++] = uucode[ix++];\n"
"            if (k &gt; 126)\n"
"                goto error_exit;\n"
"        }\n"
"        acSrcData[k++] = 0;\n"
"        if (uucode[ix])\n"
"            ix++;\n"
"        if( strlen( acSrcData ) == 0 )\n"
"        {\n"
"            // continue;\n"
"        }\n"
"        else if( acSrcData[0] &gt; 32  &amp;&amp;  acSrcData[0] &lt;= 77 )\n"
"        {\n"
"            int knBytesToWrite = (int)( acSrcData[0] - 32 );\n"
"            unsigned char acDstData[45];\n"
"            int nDstIndex = 0;\n"
"\n"
"            for( int i = 1; i &lt; strlen( acSrcData ); i += 4 )\n"
"            {\n"
"                int j;\n"
"                int anSrc1[4];\n"
"\n"
"                for( j = 0; j &lt; 4; ++j )\n"
"                {\n"
"                    const char c = acSrcData[i + j];\n"
"\n"
"                    if( c &lt; 32  ||  c &gt; 96 )\n"
"                    {\n"
"                        break;\n"
"                    }\n"
"\n"
"                    anSrc1[j] = (int)( ( c - 32 ) &amp; 63);\n"
"                }\n"
"\n"
"                for( ; j &lt; 4; ++j )\n"
"                {\n"
"                    anSrc1[j] = 0;\n"
"                }\n"
"\n"
"                int k = 0;\n"
"\n"
"                for( j = 0; j &lt; 4; ++j )\n"
"                {\n"
"                    k |= ( anSrc1[j] &lt;&lt; ( j * 6 ) );\n"
"                }\n"
"\n"
"                for( j = 0; j &lt; 3; ++j )\n"
"                {\n"
"                    acDstData[nDstIndex++] = (unsigned char)( k &gt;&gt; ( j * 8 ) );\n"
"                }\n"
"            }\n"
"\n"
"            for (i = 0; i &lt; knBytesToWrite; i++)\n"
"                out[jx++] = acDstData[i];\n"
"        }\n"
"    }\n"
"    \n"
"    if (N)\n"
"        *N = jx;\n"
"\n"
"    return out;\n"
"    \n"
"error_exit:\n"
"    free(out);\n"
"    if (N)\n"
"        *N = -1;\n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"static FILE *file_fopen(XMLNODE *node)\n"
"{\n"
"    FILE *fp = 0;\n"
"    int len;\n"
"    const char *data;\n"
"    char *last;\n"
"    int trailing = 0;\n"
"    unsigned char *plain = 0;\n"
"    int Nplain;\n"
"    const char *type;\n"
"    \n"
"    type = xml_getattribute(node, &quot;type&quot;);\n"
"    if (!type)\n"
"        goto error_exit;\n"
"    fp = tmpfile();\n"
"    if (!fp)\n"
"        goto error_exit;\n"
"    data = xml_getdata(node);\n"
"    if (!data)\n"
"        return fp;\n"
"    len = (int) strlen(data);\n"
"    last = strrchr(data, &apos;\\n&apos;);\n"
"    if (last &amp;&amp; strwhitespace(last))\n"
"        trailing = len - (int)(last - data);\n"
"    if (len - trailing &lt; 1)\n"
"        goto error_exit;\n"
"    if (!strcmp(type, &quot;text&quot;))\n"
"    {\n"
"        if (fwrite(data + 1, 1, len - trailing - 1, fp) != len - trailing - 1)\n"
"            goto error_exit;\n"
"    }\n"
"    else if (!strcmp(type, &quot;binary&quot;))\n"
"    {\n"
"        plain = uudecodestr(data, &amp;Nplain);\n"
"        if (!plain)\n"
"            goto error_exit;\n"
"        if (fwrite(plain, 1, Nplain, fp) != Nplain)\n"
"            goto error_exit;\n"
"        free(plain);\n"
"        plain = 0;\n"
"    }\n"
"    fseek(fp, 0, SEEK_SET);\n"
"    return fp;\n"
"    \n"
"error_exit:\n"
"    free(plain);\n"
"    fclose(fp);\n"
"    return 0;\n"
"}\n"
"\n"
"int bbx_write_source (const char *source_xml, char *path, const char *source_xml_file, const char *s"
"ource_xml_name)\n"
"{\n"
"    XMLDOC *doc = 0;\n"
"    char error[1024];\n"
"    char buff[1024];\n"
"    XMLNODE *root;\n"
"    XMLNODE *node;\n"
"    const char *name;\n"
"    FILE *fpout;\n"
"    FILE *fpin;\n"
"    int ch;\n"
"    \n"
"    doc = xmldocfromstring(source_xml, error, 1024);\n"
"    if (!doc)\n"
"    {\n"
"        fprintf(stderr, &quot;%s\\n&quot;, error);\n"
"        return -1;\n"
"    }\n"
"    root = xml_getroot(doc);\n"
"    if (strcmp(xml_gettag(root), &quot;FileSystem&quot;))\n"
"        return -1;\n"
"    \n"
"    if (!root-&gt;child)\n"
"        return -1;\n"
"    if (strcmp(xml_gettag(root-&gt;child), &quot;directory&quot;))\n"
"        return -1;\n"
"    fprintf(stderr, &quot;Here\\n&quot;);\n"
"    for (node = root-&gt;child-&gt;child; node != NULL; node = node-&gt;next)\n"
"    {\n"
"        if (!strcmp(xml_gettag(node), &quot;file&quot;))\n"
"        {\n"
"            fprintf(stderr, &quot;%s\\n&quot;, xml_gettag(node));\n"
"            name = xml_getattribute(node, &quot;name&quot;);\n"
"            snprintf(buff, 1024, &quot;%s%s&quot;, path, name);\n"
"            fpout = fopen(buff, &quot;w&quot;);\n"
"            fprintf(stderr,&quot;buff %s fpour %p\\n&quot;, buff, fpout);\n"
"            if (!fpout)\n"
"                break;\n"
"            fpin = file_fopen(node);\n"
"            if (!fpin)\n"
"                break;\n"
"            if (!strcmp(name, source_xml_file))\n"
"            {\n"
"                char *escaped = texttostring(source_xml);\n"
"                if (!escaped)\n"
"                    break;\n"
"                fprintf(fpout, &quot;char %s[] = %s;\\n&quot;, source_xml_name, escaped);\n"
"                free(escaped);\n"
"            }\n"
"            else\n"
"            {\n"
"               while ((ch = fgetc(fpin)) != EOF)\n"
"                   fputc(ch, fpout);\n"
"            }\n"
"            fclose(fpout);\n"
"            fclose(fpin);\n"
"            fpout = 0;\n"
"            fpin = 0;\n"
"        }\n"
"    }\n"
"    if (fpin || fpout)\n"
"    {\n"
"        fclose(fpin);\n"
"        fclose(fpout);\n"
"        return -1;\n"
"    }\n"
"    \n"
"    return 0;\n"
"    \n"
"}\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"bbx_filesystem.c\" type=\"text\">\n"
"#include &lt;stdlib.h&gt;\n"
"#include &lt;stdio.h&gt;\n"
"#include &lt;string.h&gt;\n"
"#include &lt;ctype.h&gt;\n"
"#include &lt;assert.h&gt;\n"
"\n"
"#include &quot;xmlparser2.h&quot;\n"
"\n"
"#define BBX_FS_STDIO 1\n"
"#define BBX_FS_STRING 2\n"
"\n"
"typedef struct bbx_filesystem\n"
"{\n"
"    int mode;\n"
"    FILE *openfiles[FOPEN_MAX+1];\n"
"    int Nopenfiles;\n"
"    char *filepath;\n"
"    XMLDOC *filesystemdoc;\n"
"} BBX_FileSystem;\n"
"\n"
"#define bbx_malloc malloc\n"
"\n"
"static char *bbx_strdup(const char *str)\n"
"{\n"
"    char *answer = bbx_malloc(strlen(str) +1);\n"
"    strcpy(answer, str);\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"static const char *getfilesystemname_r(XMLNODE *node);\n"
"static char *makepath(const char *base, const char *query);\n"
"static FILE *xml_fopen(XMLDOC *doc, const char *path, const char *mode);\n"
"\n"
"BBX_FileSystem *bbx_filesystem(void)\n"
"{\n"
"    BBX_FileSystem *bbx_fs;\n"
"    int i;\n"
"    \n"
"    bbx_fs = bbx_malloc(sizeof(BBX_FileSystem));\n"
"    \n"
"    bbx_fs-&gt;mode = 0;\n"
"    for (i = 0; i &lt; FOPEN_MAX + 1; i++)\n"
"        bbx_fs-&gt;openfiles[i] = 0;\n"
"    bbx_fs-&gt;Nopenfiles = 0;\n"
"    bbx_fs-&gt;filepath = 0;;\n"
"    XMLDOC *filesystemdoc = 0;\n"
"    \n"
"    return bbx_fs;\n"
"}\n"
"\n"
"void bbx_filesystem_kill(BBX_FileSystem *bbx_fs)\n"
"{\n"
"    if (bbx_fs)\n"
"    {\n"
"        if (bbx_fs-&gt;Nopenfiles)\n"
"            fprintf(stderr, &quot;warning, exiting with open files\\n&quot;);\n"
"        killxmldoc(bbx_fs-&gt;filesystemdoc);\n"
"        free(bbx_fs-&gt;filepath);\n"
"        \n"
"        free(bbx_fs);\n"
"    }\n"
"}\n"
"\n"
"\n"
"int bbx_filesystem_set(BBX_FileSystem *bbx_fs, const char *pathorxml, int mode)\n"
"{\n"
"  char error[1024];\n"
"    \n"
"  if (bbx_fs-&gt;mode != 0)\n"
"  {\n"
"      fprintf(stderr, &quot;Baby X file system already initialised\\n&quot;);\n"
"      return -1;\n"
"  }\n"
"\n"
"  if (mode == BBX_FS_STDIO)\n"
"  {\n"
"      bbx_fs-&gt;filepath = bbx_strdup(pathorxml);\n"
"      bbx_fs-&gt;mode = mode;\n"
"  }\n"
"  else if (mode == BBX_FS_STRING)\n"
"  {\n"
"   \n"
"      bbx_fs-&gt;filesystemdoc = xmldocfromstring(pathorxml, error, 1024);\n"
"      if (!bbx_fs-&gt;filesystemdoc)\n"
"      {\n"
"          fprintf(stderr, &quot;%s\\n&quot;, error);\n"
"          return -1;\n"
"      }\n"
"      bbx_fs-&gt;mode = mode;\n"
"  }\n"
"  else\n"
"  {\n"
"      fprintf(stderr, &quot;Initialising Baby X file system in unsupported mode\\n&quot;);\n"
"      return -1;\n"
"  }\n"
"    \n"
"  return 0;\n"
"}\n"
"\n"
"FILE *bbx_filesystem_fopen(BBX_FileSystem *bbx_fs, const char *path, const char *mode)\n"
"{\n"
"  FILE *fp = 0;\n"
"  char *stdpath = 0;\n"
"    \n"
"  if (bbx_fs-&gt;Nopenfiles &gt;= FOPEN_MAX)\n"
"  {\n"
"      fprintf(stderr, &quot;Baby X file system, too many open files\\n&quot;);\n"
"      return 0;\n"
"  }\n"
"\n"
"  if (strcmp(mode, &quot;r&quot;))\n"
"  {\n"
"      fprintf(stderr, &quot;Baby X files system, fopen only support for reading \\&quot;r\\&quot; mode"
"&quot;);\n"
"      return 0;\n"
"  }\n"
"\n"
"  if (bbx_fs-&gt;mode == BBX_FS_STDIO)\n"
"  {\n"
"     stdpath = makepath(bbx_fs-&gt;filepath, path);\n"
"     fp = fopen(stdpath, mode);\n"
"     free(stdpath);\n"
"    \n"
"     if (fp)\n"
"         bbx_fs-&gt;openfiles[bbx_fs-&gt;Nopenfiles++] = fp;\n"
"\n"
"     return fp;\n"
"  }\n"
"  else if (bbx_fs-&gt;mode == BBX_FS_STRING)\n"
"  {\n"
"      fp = xml_fopen(bbx_fs-&gt;filesystemdoc, path, mode);\n"
"      \n"
"       if (fp)\n"
"         bbx_fs-&gt;openfiles[bbx_fs-&gt;Nopenfiles++] = fp;\n"
"\n"
"       return fp;\n"
"  }\n"
"  else\n"
"  {\n"
"      fprintf(stderr, &quot;BabyX file system not intialised correctly\\n&quot;);\n"
"      return 0;\n"
"  }\n"
"} \n"
"\n"
"int bbx_filesystem_fclose(BBX_FileSystem *bbx_fs, FILE *fp)\n"
"{\n"
"   int i, j;\n"
"\n"
"   if (bbx_fs-&gt;mode == BBX_FS_STDIO || bbx_fs-&gt;mode == BBX_FS_STRING)\n"
"   {\n"
"      if (fp == NULL)\n"
"         return 0;\n"
"\n"
"      for (i = 0; i &lt; bbx_fs-&gt;Nopenfiles; i++)\n"
"      {\n"
"         if (bbx_fs-&gt;openfiles[i] == fp)\n"
"         {\n"
"            for (j = i + 1; j &lt; bbx_fs-&gt;Nopenfiles + 1; j++)\n"
"              bbx_fs-&gt;openfiles[j-1] = bbx_fs-&gt;openfiles[j];\n"
"            bbx_fs-&gt;Nopenfiles--;\n"
"            return fclose (fp);\n"
"         }\n"
"      }\n"
"      fprintf(stderr, &quot;bbx_filesystem_fclose, failed to close file\\n&quot;);\n"
"      assert(0);\n"
"      return -1;  \n"
"   }\n"
"     \n"
"   fprintf(stderr, &quot;bbx_filessytem_fclose, file system not initialised\\n&quot;);\n"
"   assert(0);\n"
"   return -1;\n"
"}\n"
"\n"
"const char *bbx_filesystem_getname(BBX_FileSystem *bbx_fs)\n"
"{\n"
"    const char *answer = &quot;&quot;;\n"
"    \n"
"    if (bbx_fs-&gt;mode == BBX_FS_STDIO)\n"
"    {\n"
"        answer = strrchr(bbx_fs-&gt;filepath, &apos;/&apos;);\n"
"        if (answer)\n"
"            answer = answer + 1;\n"
"        else\n"
"            answer = bbx_fs-&gt;filepath;\n"
"        \n"
"    }\n"
"    else\n"
"    {\n"
"        answer = getfilesystemname_r(xml_getroot(bbx_fs-&gt;filesystemdoc));\n"
"    }\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"   THec FileSystem node should have one child, which is a directory with the name of the directory p"
"assed to directorytoxml\n"
"*/\n"
"static const char *getfilesystemname_r(XMLNODE *node)\n"
"{\n"
"    const char *answer = 0;\n"
"    XMLNODE *child;\n"
"    \n"
"    while (node)\n"
"    {\n"
"        if (!strcmp(xml_gettag(node), &quot;FileSystem&quot;))\n"
"        {\n"
"            for (child = node-&gt;child; child != NULL; child = child-&gt;next)\n"
"            {\n"
"                if (!strcmp(xml_gettag(child), &quot;directory&quot;))\n"
"                {\n"
"                    if (xml_getattribute(child, &quot;name&quot;))\n"
"                    {\n"
"                        answer = xml_getattribute(child, &quot;name&quot;);\n"
"                        break;\n"
"                    }\n"
"                }\n"
"            }\n"
"            break;\n"
"        }\n"
"        if (answer == 0)\n"
"            answer = getfilesystemname_r(node-&gt;child);\n"
"        else\n"
"            break;\n"
"        node = node-&gt;next;\n"
"    }\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"\n"
"/*\n"
"    The external directory mounted should be of the form\n"
"         &quot;/users/fred/babyxdevelopment/mydir&quot;\n"
"     The query should be of the form\n"
"         &quot;/mydir/readme.txt&quot;\n"
" */\n"
"static char *makepath(const char *base, const char *query)\n"
"{\n"
"    const char *local;\n"
"    char *answer;\n"
"    int i;\n"
"    \n"
"    local = strrchr(base, &apos;/&apos;);\n"
"    if (!local)\n"
"        local = base;\n"
"    \n"
"    if (strncmp(local, query, strlen(local)))\n"
"    {\n"
"        fprintf(stderr, &quot;Baby X files system, bad path, mounted %s query %s\\n&quot;,\n"
"                local, query);\n"
"        return 0;\n"
"    }\n"
"    if (query[strlen(local)] != &apos;/&apos;)\n"
"    {\n"
"        fprintf(stderr, &quot;Baby X files system, bad path, mounted %s query %s\\n&quot;,\n"
"                local, query);\n"
"        return 0;\n"
"    }\n"
"    \n"
"    answer = bbx_malloc(strlen(base) + strlen(query) + 1);\n"
"    for (i = 0; base + i != local; i++)\n"
"    {\n"
"        answer[i] = base[i];\n"
"    }\n"
"    strcpy(answer + i, query);\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"    This program is designed to show off the capabilities of the XML Parser.\n"
" \n"
"     We generate an XML file which represents a filesystem. Then we query it for files.\n"
"     Unfortunatley there is no way to traverse a physical computer&apos;s filesystem with\n"
"     complete portablity, and so the encoder, directorytoxml, will not run everywhere.\n"
"     But the decoder is completetely portable. And you can incorporate it into your own\n"
"     programs to have embeded files. Use the Baby X resource compiler to convert the XML\n"
"     to a string, then load it using xmldocfromstring.\n"
" \n"
"     The XML is very simple\n"
"     \n"
"     &lt;FileSystem&gt;\n"
"           &lt;directory name=&quot;poems&quot;&gt;\n"
"               &lt;directory name=&quot;Shakespeare&quot;&gt;\n"
"                    &lt;file name=&quot;Sonnet 12&quot; type=&quot;text&quot;&gt;\n"
"                        When do I count the clock that tells the time\?\n"
"                    &lt;/file&gt;\n"
"                &lt;/directory&gt;\n"
"                &lt;directory name=&quot;Blake&quot;&gt;\n"
"                    &lt;file name=&quot;Tyger&quot; tyoe=&quot;text&quot;&gt;\n"
"                            Tyger, tyger, burning bright,\n"
"                            Through the forests of the night,\n"
"                    &lt;/file&gt;\n"
"                    &lt;file name=&quot;BlakePicture.png&quot; type=&quot;binary&quot;&gt;\n"
" &lt;![CDATA[M)&quot;E3&apos;U@&quot;:H````0#)A$12!``#H`````6(8````056&quot;`0``@&quot;HFV0#!52#-$(\n"
" M0)W;FE&amp;;E!``(E8E7&gt;`43EN%&apos;_[&gt;3/D`$2(&quot;(E0-4$D.!0*A&gt;H((=04)D$(A\n"
" M2,$(HBHB(+N&quot;L60$1PR*ZJBH@K*UU*6P&quot;+*6PN;06$1==Q&quot;V0EW-P08W]-OW\n"
" ]]&gt;\n"
"                    &lt;/file&gt;\n"
"                &lt;/directory&gt;\n"
"           &lt;/directory&gt;\n"
"     &lt;/FilesSystem&gt;\n"
" \n"
"     All the code was written by Malcolm  McLean.\n"
"      It is free for any use.\n"
" */\n"
"\n"
"/*\n"
"   Does a string cnsist entirely of white space\? (also treat nulls as white)\n"
" */\n"
"static int strwhitespace(const char *str)\n"
"{\n"
"    if (!str)\n"
"        return 1;\n"
"    while (*str)\n"
"    {\n"
"        if (!isspace((unsigned char) *str))\n"
"            return  0;\n"
"        str++;\n"
"    }\n"
"    \n"
"    return 1;\n"
"}\n"
"\n"
"/*\n"
" The functions\n"
" \n"
" unsigned char *uudecodestr(const char *uucode, int *N)\n"
" char *uuencodestr(const unsigned char *binary, int N)\n"
" \n"
" Have been modifed by Malcolm McLean from the functions\n"
" \n"
" void UUEncode( char * pSrcFilename, FILE * pSrcFile, FILE * pDstFile )\n"
" bool UUDecode( FILE * pSrcFile )\n"
" \n"
" by Tom Weatherhead.\n"
" \n"
" They appear in Tom&apos;s github project &quot;helix&quot;.\n"
" \n"
"The MIT License (MIT)\n"
"\n"
"Copyright (c) 2002-2021 Tom Weatherhead\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the &quot;Software&quot;), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
"SOFTWARE.\n"
" \n"
" */\n"
"\n"
"static unsigned char *uudecodestr(const char *uucode, int *N)\n"
"{\n"
"    int Nmaxout;\n"
"    unsigned char *out;\n"
"    int ix = 0;\n"
"    int jx = 0;\n"
"    int k;\n"
"    int i;\n"
"    \n"
"    Nmaxout = ((int)strlen(uucode)/60 +1) * 45;\n"
"    out = malloc(Nmaxout);\n"
"    \n"
"    while( uucode[ix] )\n"
"    {\n"
"        // Get a line of the src text file.\n"
"        char acSrcData[128];\n"
"        \n"
"        k = 0;\n"
"        while (uucode[ix] != &apos;\\n&apos; &amp;&amp; uucode[ix])\n"
"        {\n"
"            acSrcData[k++] = uucode[ix++];\n"
"            if (k &gt; 126)\n"
"                goto error_exit;\n"
"        }\n"
"        acSrcData[k++] = 0;\n"
"        if (uucode[ix])\n"
"            ix++;\n"
"        if( strlen( acSrcData ) == 0 )\n"
"        {\n"
"            // continue;\n"
"        }\n"
"        else if( acSrcData[0] &gt; 32  &amp;&amp;  acSrcData[0] &lt;= 77 )\n"
"        {\n"
"            int knBytesToWrite = (int)( acSrcData[0] - 32 );\n"
"            unsigned char acDstData[45];\n"
"            int nDstIndex = 0;\n"
"\n"
"            for( int i = 1; i &lt; strlen( acSrcData ); i += 4 )\n"
"            {\n"
"                int j;\n"
"                int anSrc1[4];\n"
"\n"
"                for( j = 0; j &lt; 4; ++j )\n"
"                {\n"
"                    const char c = acSrcData[i + j];\n"
"\n"
"                    if( c &lt; 32  ||  c &gt; 96 )\n"
"                    {\n"
"                        break;\n"
"                    }\n"
"\n"
"                    anSrc1[j] = (int)( ( c - 32 ) &amp; 63);\n"
"                }\n"
"\n"
"                for( ; j &lt; 4; ++j )\n"
"                {\n"
"                    anSrc1[j] = 0;\n"
"                }\n"
"\n"
"                int k = 0;\n"
"\n"
"                for( j = 0; j &lt; 4; ++j )\n"
"                {\n"
"                    k |= ( anSrc1[j] &lt;&lt; ( j * 6 ) );\n"
"                }\n"
"\n"
"                for( j = 0; j &lt; 3; ++j )\n"
"                {\n"
"                    acDstData[nDstIndex++] = (unsigned char)( k &gt;&gt; ( j * 8 ) );\n"
"                }\n"
"            }\n"
"\n"
"            for (i = 0; i &lt; knBytesToWrite; i++)\n"
"                out[jx++] = acDstData[i];\n"
"        }\n"
"    }\n"
"    \n"
"    if (N)\n"
"        *N = jx;\n"
"\n"
"    return out;\n"
"    \n"
"error_exit:\n"
"    free(out);\n"
"    if (N)\n"
"        *N = -1;\n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"static char *directoryname(const char *path, int pos)\n"
"{\n"
"    int i;\n"
"    int j = 0;\n"
"    int len = 0;\n"
"    char *answer = 0;\n"
"    \n"
"    for(i = pos + 1; path[i]; i++)\n"
"        if(path[i] == &apos;/&apos;)\n"
"            break;\n"
"    len = i - pos - 1;\n"
"    answer = malloc(len + 1);\n"
"    if (!answer)\n"
"        goto out_of_memory;\n"
"    for(i = pos + 1; path[i]; i++)\n"
"    {\n"
"        if(path[i] == &apos;/&apos;)\n"
"            break;\n"
"        answer[j++] = path[i];\n"
"    }\n"
"    answer[j++] = 0;\n"
"    return answer;\n"
"out_of_memory:\n"
"    return 0;\n"
"}\n"
"\n"
"static FILE *file_fopen(XMLNODE *node)\n"
"{\n"
"    FILE *fp = 0;\n"
"    int len;\n"
"    const char *data;\n"
"    char *last;\n"
"    int trailing = 0;\n"
"    unsigned char *plain = 0;\n"
"    int Nplain;\n"
"    const char *type;\n"
"    \n"
"    type = xml_getattribute(node, &quot;type&quot;);\n"
"    if (!type)\n"
"        goto error_exit;\n"
"    fp = tmpfile();\n"
"    if (!fp)\n"
"        goto error_exit;\n"
"    data = xml_getdata(node);\n"
"    if (!data)\n"
"        return fp;\n"
"    len = (int) strlen(data);\n"
"    last = strrchr(data, &apos;\\n&apos;);\n"
"    if (last &amp;&amp; strwhitespace(last))\n"
"        trailing = len - (int)(last - data);\n"
"    if (len - trailing &lt; 1)\n"
"        goto error_exit;\n"
"    if (!strcmp(type, &quot;text&quot;))\n"
"    {\n"
"        if (fwrite(data + 1, 1, len - trailing - 1, fp) != len - trailing - 1)\n"
"            goto error_exit;\n"
"    }\n"
"    else if (!strcmp(type, &quot;binary&quot;))\n"
"    {\n"
"        plain = uudecodestr(data, &amp;Nplain);\n"
"        if (!plain)\n"
"            goto error_exit;\n"
"        if (fwrite(plain, 1, Nplain, fp) != Nplain)\n"
"            goto error_exit;\n"
"        free(plain);\n"
"        plain = 0;\n"
"    }\n"
"    fseek(fp, 0, SEEK_SET);\n"
"    return fp;\n"
"    \n"
"error_exit:\n"
"    free(plain);\n"
"    fclose(fp);\n"
"    return 0;\n"
"}\n"
"\n"
"static FILE *directory_fopen_r(XMLNODE *node, const char *path, int pos)\n"
"{\n"
"    FILE *answer = 0;\n"
"    const char *nodename;\n"
"    char *name = 0;\n"
"    XMLNODE *child;\n"
"    int lastdir;\n"
"    \n"
"    name = directoryname(path, pos);\n"
"    while (node)\n"
"    {\n"
"        if (!strcmp(xml_gettag(node), &quot;directory&quot;))\n"
"        {\n"
"            nodename = xml_getattribute(node, &quot;name&quot;);\n"
"            if (nodename &amp;&amp; !strcmp(name, nodename))\n"
"            {\n"
"                answer = directory_fopen_r(node-&gt;child, path, pos + (int) strlen(name) + 1);\n"
"            }\n"
"        }\n"
"        else if(!strcmp(xml_gettag(node), &quot;file&quot;))\n"
"        {\n"
"            nodename = xml_getattribute(node, &quot;name&quot;);\n"
"            if (nodename &amp;&amp; !strcmp(name, nodename))\n"
"            {\n"
"                answer = file_fopen(node);\n"
"                break;\n"
"            }\n"
"        }\n"
"        \n"
"        node = node-&gt;next;\n"
"    }\n"
"    \n"
"    free(name);\n"
"    return  answer;\n"
"}\n"
"\n"
"static FILE *xml_fopen_r(XMLNODE *node, const char *path)\n"
"{\n"
"    FILE *answer = 0;\n"
"    while (node)\n"
"    {\n"
"        if (!strcmp(xml_gettag(node), &quot;FileSystem&quot;))\n"
"        {\n"
"            answer = directory_fopen_r(node-&gt;child, path, 0);\n"
"            if (answer)\n"
"                break;\n"
"        }\n"
"        if (node-&gt;child)\n"
"        {\n"
"            answer = xml_fopen_r(node-&gt;child, path);\n"
"            if (answer)\n"
"                break;\n"
"        }\n"
"        node = node-&gt;next;\n"
"    }\n"
"    \n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"   Query an XML document for files under FileSystem tag.\n"
" \n"
"   You will need to take this function if using the FileSystem to\n"
"     embed files into your own programs.\n"
" \n"
" */\n"
"static FILE *xml_fopen(XMLDOC *doc, const char *path, const char *mode)\n"
"{\n"
"    return xml_fopen_r(xml_getroot(doc), path);\n"
"}\n"
"\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"asciitostring.c\" type=\"text\">\n"
"#include &lt;stdio.h&gt;\n"
"#include &lt;stdlib.h&gt;\n"
"#include &lt;string.h&gt;\n"
"#include &lt;limits.h&gt;\n"
"#include &lt;ctype.h&gt;\n"
"\n"
"#include &quot;asciitostring.h&quot;\n"
"\n"
"static size_t linesbiggerthan(const char *str, size_t maxlen);\n"
"static int escaped(int ch);\n"
"static char escapechar(int ch);\n"
"\n"
"/*\n"
"  load a text file into memory\n"
"\n"
"*/\n"
"char *fslurp(FILE *fp)\n"
"{\n"
"  char *answer;\n"
"  char *temp;\n"
"  int buffsize = 1024;\n"
"  int i = 0;\n"
"  int ch;\n"
"\n"
"  answer = malloc(1024);\n"
"  if(!answer)\n"
"    return 0;\n"
"  while( (ch = fgetc(fp)) != EOF )\n"
"  {\n"
"    if(i == buffsize-2)\n"
"    {\n"
"      if(buffsize &gt; INT_MAX - 100 - buffsize/10)\n"
"      {\n"
"\tfree(answer);\n"
"        return 0;\n"
"      }\n"
"      buffsize = buffsize + 100 * buffsize/10;\n"
"      temp = realloc(answer, buffsize);\n"
"      if(temp == 0)\n"
"      {\n"
"        free(answer);\n"
"        return 0;\n"
"      }\n"
"      answer = temp;\n"
"    }\n"
"    answer[i++] = (char) ch;\n"
"  }\n"
"  answer[i++] = 0;\n"
"\n"
"  temp = realloc(answer, i);\n"
"  if(temp)\n"
"    return temp;\n"
"  else\n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"  test if a string is a valid C string - can it go in the place\n"
"   of a C string literal\?\n"
" */\n"
"int validcstring(const char *str)\n"
"{\n"
"  size_t len;\n"
"  size_t i, j;\n"
"  size_t start;\n"
"  size_t end;\n"
"\n"
"  len = strlen(str);\n"
"  if(len &lt; 2)\n"
"    return 0;\n"
"  for(start=0; start &lt; len; start++)\n"
"    if(!isspace( (unsigned char) str[start]))\n"
"      break;\n"
"  end = len;\n"
"  while(end--)\n"
"    if(!isspace((unsigned char) str[end]))\n"
"      break;\n"
"  if(start == len || end == (size_t) -1)\n"
"    return 0;\n"
"  if(str[start] == &apos;L&apos;)\n"
"    start++;\n"
"  if(start == end || str[start] != &apos;\\&quot;&apos; || str[end] != &apos;\\&quot;&apos;)\n"
"    return 0;\n"
"  start++;\n"
"  end--;\n"
"\n"
"  for(i=start;i&lt;=end;i++)\n"
"  {\n"
"    if(str[i] == &apos;\\\\&apos;)\n"
"    {\n"
"      if(i == end)\n"
"        return 0;\n"
"      if(strchr(&quot;aftbrnvxou01234567\\\?\\\\\\&apos;\\&quot;&quot;, str[i+1]))\n"
"      {\n"
"        i++;\n"
"        continue;\n"
"      }\n"
"      else\n"
"        return 0;\n"
"    }\n"
"      \n"
"    if(str[i] == &apos;\\&quot;&apos;)\n"
"    {\n"
"      for(j=i+1;j&lt;end;j++)\n"
"        if(!isspace((unsigned char) str[j]))\n"
"          break;\n"
"      if(str[j] == &apos;\\&quot;&apos;)\n"
"        i = j;\n"
"      else\n"
"        return 0;\n"
"     }\n"
"     if(!isgraph( (unsigned char) str[i]) &amp;&amp; str[i] != &apos; &apos;)\n"
"       return 0;\n"
"  }\n"
"  \n"
"  return 1;\n"
"}\n"
"\n"
"\n"
"/*\n"
"  convert a string to a C language string;\n"
"  Params:\n"
"    str - the string to convert\n"
"  Returns: C version of string, 0 on out of memory\n"
"  Notes: newlines are represented by breaks in the string.\n"
"*/\n"
"char *texttostring(const char *str)\n"
"{\n"
"  size_t len = 0;\n"
"  size_t i;\n"
"  size_t j = 0;\n"
"  size_t linelen = 0;\n"
"  char *answer;\n"
"\n"
"  for(i=0;str[i];i++)\n"
"  {\n"
"    if(str[i] == &apos;\\n&apos;)\n"
"      len += 5;\n"
"    else if(escaped(str[i]))\n"
"      len+=2;\n"
"   else\n"
"     len += 1;\n"
"  }\n"
"  len += linesbiggerthan(str, 100) * 3;\n"
"  len++;\n"
"  len += 2;\n"
"  answer = malloc(len);\n"
"  if(!answer)\n"
"    return 0;\n"
"  answer[j++] = &apos;&quot;&apos;;\n"
"  for(i=0;str[i];i++)\n"
"  {\n"
"    if(str[i] == &apos;\\n&apos; &amp;&amp; str[i+1] != 0)\n"
"    {\n"
"      answer[j++] = &apos;\\\\&apos;;\n"
"      answer[j++] = &apos;n&apos;;\n"
"      answer[j++] = &apos;\\&quot;&apos;;\n"
"      answer[j++] = &apos;\\n&apos;;\n"
"      answer[j++] = &apos;\\&quot;&apos;;\n"
"      linelen = 0;\n"
"    }\n"
"    else if(escaped(str[i]))\n"
"    {\n"
"      answer[j++] = &apos;\\\\&apos;;\n"
"      answer[j++] = escapechar(str[i]);\n"
"      linelen++;\n"
"    }\n"
"    else\n"
"    {\n"
"      answer[j++] = str[i];\n"
"      linelen++;\n"
"    }\n"
"    if(linelen == 100 &amp;&amp; str[i+1] != &apos;\\n&apos;)\n"
"    {\n"
"      answer[j++] = &apos;\\&quot;&apos;;\n"
"      answer[j++] = &apos;\\n&apos;;\n"
"      answer[j++] = &apos;\\&quot;&apos;;\n"
"      linelen = 0;\n"
"    }\n"
"  }\n"
"  answer[j++] = &apos;\\&quot;&apos;;\n"
"  answer[j++] = 0;\n"
"\n"
"  return answer;\n"
"\n"
"}\n"
"\n"
"/*\n"
"  test if a character is escaped in C\n"
"  Params: ch - the character to test\n"
"  Returns: 1 if escaped in C strings, else 0\n"
"*/\n"
"static int escaped(int ch)\n"
"{\n"
"  char *escapes = &quot;\\a\\b\\f\\n\\r\\t\\v\\\?\\&apos;\\&quot;\\\\&quot;;\n"
"\n"
"  if(ch == 0)\n"
"    return 1;\n"
"  return strchr(escapes, ch) \? 1 : 0;\n"
"\n"
"}\n"
"\n"
"/*\n"
"  get the escape character to represent ch\n"
"  Params: ch - an escaped character\n"
"  Returns: character that stands in for it in esacpe sequence,\n"
"    0 if ch is not an escaped character\n"
"*/\n"
"static char escapechar(int ch)\n"
"{\n"
"  char *escapes = &quot;\\a\\b\\f\\n\\r\\t\\v\\\?\\&apos;\\&quot;\\\\&quot;;\n"
"  char *characters = &quot;abfnrtv\?\\&apos;\\&quot;\\\\&quot;;\n"
"  char *ptr;\n"
"\n"
"  if(ch == 0)\n"
"    return &apos;0&apos;;\n"
"  ptr = strchr(escapes, ch);\n"
"  if(ptr)\n"
"    return characters[ptr - escapes];\n"
"  else\n"
"    return 0;\n"
"\n"
"}\n"
"\n"
"/*\n"
"  get the number of lines bigger than a certain value\n"
"*/\n"
"static size_t linesbiggerthan(const char *str, size_t maxlen)\n"
"{\n"
"  size_t len = 0;\n"
"  size_t answer = 0;\n"
"\n"
"  while(*str)\n"
"  {\n"
"    if(*str == &apos;\\n&apos;)\n"
"     len = 0;\n"
"    else\n"
"    {\n"
"      len++;\n"
"      if(len &gt; maxlen)\n"
"      {\n"
"       len = 0;\n"
"       answer++;\n"
"      }\n"
"    }\n"
"     str++;\n"
"   }\n"
"\n"
"  return answer;\n"
"}\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"source.c\" type=\"text\">\n"
"//\n"
"//  source.c\n"
"//  testbabyxfilesystem\n"
"//\n"
"//  Created by Malcolm McLean on 01/06/2024.\n"
"//\n"
"char source[] = &quot;placeholder&quot;;\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"bbx_write_source.h\" type=\"text\">\n"
"//\n"
"//  bbx_write_source.h\n"
"//  testbabyxfilesystem\n"
"//\n"
"//  Created by Malcolm McLean on 31/05/2024.\n"
"//\n"
"\n"
"#ifndef bbx_write_source_h\n"
"#define bbx_write_source_h\n"
"\n"
"int bbx_write_source (const char *source_xml, char *path, const char *source_xml_file, const char *s"
"ource_xml_name);\n"
"\n"
"#endif /* bbx_write_source_h */\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"xmlparser2.h\" type=\"text\">\n"
"#ifndef xmlparser_h\n"
"#define xmlparser_h\n"
"\n"
"#include &lt;stdio.h&gt;\n"
"\n"
"typedef struct xmlattribute\n"
"{\n"
"  char *name;                /* attribute name */\n"
"  char *value;               /* attribute value (without quotes) */\n"
"  struct xmlattribute *next; /* next pointer in linked list */\n"
"} XMLATTRIBUTE;\n"
"\n"
"typedef struct xmlnode\n"
"{\n"
"  char *tag;                 /* tag to identify data type */\n"
"  XMLATTRIBUTE *attributes;  /* attributes */\n"
"  char *data;                /* data as ascii */\n"
"  int position;              /* position of the node within parent&apos;s data string */\n"
"  int lineno;                /* line number of node in document */\n"
"  struct xmlnode *next;      /* sibling node */\n"
"  struct xmlnode *child;     /* first child node */\n"
"} XMLNODE;\n"
"\n"
"typedef struct\n"
"{\n"
"  XMLNODE *root;             /* the root node */\n"
"} XMLDOC;\n"
"\n"
"\n"
"XMLDOC *loadxmldoc(const char *fname, char *errormessage, int Nerr);\n"
"XMLDOC *floadxmldoc(FILE *fp, char *errormessage, int Nerr);\n"
"XMLDOC *xmldocfromstring(const char *str,char *errormessage, int Nerr);\n"
"void killxmldoc(XMLDOC *doc);\n"
"void killxmlnode(XMLNODE *node);\n"
"\n"
"XMLNODE *xml_getroot(XMLDOC *doc);\n"
"const char *xml_gettag(XMLNODE *node);\n"
"const char *xml_getdata(XMLNODE *node);\n"
"const char *xml_getattribute(XMLNODE *node, const char *attr);\n"
"int xml_Nchildren(XMLNODE *node);\n"
"int xml_Nchildrenwithtag(XMLNODE *node, const char *tag);\n"
"XMLNODE *xml_getchild(XMLNODE *node, const char *tag, int index);\n"
"XMLNODE **xml_getdescendants(XMLNODE *node, const char *tag, int *N);\n"
"char *xml_getnesteddata(XMLNODE *node);\n"
"\n"
"int xml_getlineno(XMLNODE *node);\n"
"XMLATTRIBUTE *xml_unknownattributes(XMLNODE *node, ...);\n"
"\n"
"#endif\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"directorytoxml.c\" type=\"text\">\n"
"#include &lt;stdio.h&gt;\n"
"#include &lt;stdlib.h&gt;\n"
"#include &lt;errno.h&gt;\n"
"#include &lt;string.h&gt;\n"
"#include &lt;limits.h&gt;\n"
"\n"
"#include &lt;dirent.h&gt;\n"
"#include &lt;sys/types.h&gt;\n"
"#include &lt;sys/stat.h&gt;\n"
"#include &lt;unistd.h&gt;\n"
"\n"
"/*\n"
" strdup drop in replacement\n"
" */\n"
"static char *mystrdup(const char *str)\n"
"{\n"
"  char *answer;\n"
"\n"
"  answer = malloc(strlen(str) + 1);\n"
"  if(answer)\n"
"    strcpy(answer, str);\n"
"\n"
"  return answer;\n"
"}\n"
"\n"
"/*\n"
"    Concatenate two strings, returning an allocated result.\n"
" */\n"
"static char *mystrconcat(const char *prefix, const char *suffix)\n"
"{\n"
"    int lena, lenb;\n"
"    char *answer;\n"
"    \n"
"    lena = (int) strlen(prefix);\n"
"    lenb = (int) strlen(suffix);\n"
"    answer = malloc(lena + lenb + 1);\n"
"    if (answer)\n"
"    {\n"
"        strcpy(answer, prefix);\n"
"        strcpy(answer + lena, suffix);\n"
"    }\n"
"    return  answer;\n"
"}\n"
"\n"
"\n"
"/*\n"
"   get the filename at the end if a path\n"
" */\n"
"static char *getfilename(const char *path)\n"
"{\n"
"    const char *answer;\n"
"    \n"
"    answer = strrchr(path, &apos;/&apos;);\n"
"    if (answer)\n"
"        answer = answer + 1;\n"
"    else\n"
"        answer = path;\n"
"    \n"
"    return mystrdup(answer);\n"
"}\n"
"\n"
"/*\n"
"  load a text file into memory\n"
"\n"
"*/\n"
"static char *fslurp(FILE *fp)\n"
"{\n"
"  char *answer;\n"
"  char *temp;\n"
"  int buffsize = 1024;\n"
"  int i = 0;\n"
"  int ch;\n"
"\n"
"  answer = malloc(1024);\n"
"  if(!answer)\n"
"    return 0;\n"
"  while( (ch = fgetc(fp)) != EOF )\n"
"  {\n"
"    if(i == buffsize-2)\n"
"    {\n"
"      if(buffsize &gt; INT_MAX - 100 - buffsize/10)\n"
"      {\n"
"    free(answer);\n"
"        return 0;\n"
"      }\n"
"      buffsize = buffsize + 100 * buffsize/10;\n"
"      temp = realloc(answer, buffsize);\n"
"      if(temp == 0)\n"
"      {\n"
"        free(answer);\n"
"        return 0;\n"
"      }\n"
"      answer = temp;\n"
"    }\n"
"    answer[i++] = (char) ch;\n"
"  }\n"
"  answer[i++] = 0;\n"
"\n"
"  temp = realloc(answer, i);\n"
"  if(temp)\n"
"    return temp;\n"
"  else\n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"  Load a binary file into memory\n"
" */\n"
"static unsigned char *slurpb(const char *fname, int *len)\n"
"{\n"
"    FILE *fp;\n"
"    unsigned char *answer = 0;\n"
"    unsigned char *temp;\n"
"    int capacity = 1024;\n"
"    int N = 0;\n"
"    int ch;\n"
"\n"
"    fp = fopen(fname, &quot;rb&quot;);\n"
"    if (!fp)\n"
"    {\n"
"        *len = -2;\n"
"        return 0;\n"
"    }\n"
"    answer = malloc(capacity);\n"
"    if (!answer)\n"
"        goto out_of_memory;\n"
"    while ( (ch = fgetc(fp)) != EOF)\n"
"    {\n"
"        answer[N++] = ch;\n"
"        if (N &gt;= capacity)\n"
"        {\n"
"            if (capacity &gt; INT_MAX/2)\n"
"                goto  out_of_memory;\n"
"            temp = realloc(answer, capacity + capacity / 2);\n"
"            if (!temp)\n"
"                goto out_of_memory;\n"
"            answer = temp;\n"
"            capacity = capacity + capacity / 2;\n"
"        }\n"
"      \n"
"    }\n"
"    *len = N;\n"
"    fclose(fp);\n"
"    return answer;\n"
"out_of_memory:\n"
"    fclose(fp);\n"
"    *len = -1;\n"
"    free(answer);\n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"/*\n"
"  is a file binary\?\n"
" */\n"
"static int is_binary(const char *path)\n"
"{\n"
"    int answer = 0;\n"
"    int ch;\n"
"    FILE *fp;\n"
"    \n"
"    fp = fopen(path, &quot;rb&quot;);\n"
"    if (!fp)\n"
"        return 0;\n"
"    while ((ch = fgetc(fp)) != EOF)\n"
"    {\n"
"      if (ch &lt; 32)\n"
"      {\n"
"          if (ch != &apos;\\t&apos; &amp;&amp; ch != &apos;\\n&apos; &amp;&amp; ch != &apos;\\r&apos;)\n"
"          {\n"
"              answer = 1;\n"
"              break;\n"
"          }\n"
"      }\n"
"       if (ch &gt; 127)\n"
"       {\n"
"           answer = 1;\n"
"           break;\n"
"       }\n"
"    }\n"
"    fclose(fp);\n"
"    return answer;\n"
"}\n"
"\n"
"/*\n"
"    is a file a regular file \?\n"
" */\n"
"static int is_regular_file(const char *path)\n"
"{\n"
"    struct stat path_stat;\n"
"    stat(path, &amp;path_stat);\n"
"    return S_ISREG(path_stat.st_mode);\n"
"}\n"
"\n"
"/*\n"
"  is a file a directory \?\n"
" */\n"
"static int is_directory(const char *path)\n"
"{\n"
"   struct stat statbuf;\n"
"   if (stat(path, &amp;statbuf) != 0)\n"
"       return 0;\n"
"   return S_ISDIR(statbuf.st_mode);\n"
"}\n"
"\n"
"/*\n"
"   Escape a string to write as XML\n"
" */\n"
"static char *xml_escape(const char *data)\n"
"{\n"
"    int i;\n"
"    int size = 0;\n"
"    char *answer;\n"
"    char *ptr;\n"
"    \n"
"    for (i = 0; data[i]; i++)\n"
"    {\n"
"        switch(data[i]) {\n"
"            case &apos;&amp;&apos;:  size += 5; break;\n"
"            case &apos;\\&quot;&apos;: size += 6; break;\n"
"            case &apos;\\&apos;&apos;: size += 6; break;\n"
"            case &apos;&lt;&apos;:  size += 4; break;\n"
"            case &apos;&gt;&apos;:  size += 4; break;\n"
"            default:   size += 1; break;\n"
"        }\n"
"    }\n"
"    answer = malloc(size+1);\n"
"    if (!answer)\n"
"        goto out_of_memory;\n"
"    \n"
"    ptr = answer;\n"
"    for (i = 0; data[i]; i++) {\n"
"        switch(data[i]) {\n"
"            case &apos;&amp;&apos;:  strcpy(ptr, &quot;&amp;amp;&quot;); ptr += 5;     break;\n"
"            case &apos;\\&quot;&apos;: strcpy(ptr, &quot;&amp;quot;&quot;); ptr += 6;    break;\n"
"            case &apos;\\&apos;&apos;: strcpy(ptr, &quot;&amp;apos;&quot;); ptr += 6;     break;\n"
"            case &apos;&lt;&apos;:  strcpy(ptr, &quot;&amp;lt;&quot;); ptr += 4;      break;\n"
"            case &apos;&gt;&apos;:  strcpy(ptr, &quot;&amp;gt;&quot;); ptr += 4;      break;\n"
"            default:   *ptr++ = data[i]; break;\n"
"        }\n"
"    }\n"
"    *ptr++ = 0;\n"
"    \n"
"    return answer;\n"
"out_of_memory:\n"
"    return 0;\n"
"}\n"
"\n"
"static int xml_writecdata(FILE *fp, const char *data)\n"
"{\n"
"    int i;\n"
"    int err;\n"
"    \n"
"    err = fprintf(fp, &quot;&lt;![CDATA[&quot;);\n"
"    if (err &lt; 0)\n"
"        return -1;\n"
"    for (i = 0; data[i]; i++)\n"
"    {\n"
"        err = fputc(data[i], fp);\n"
"        if (err == EOF)\n"
"            return -1;\n"
"        if (data[i] == &apos;]&apos; &amp;&amp; data[i+1] == &apos;]&apos; &amp;&amp; data[i+2] == &"
"apos;&gt;&apos;)\n"
"        {\n"
"            i++;\n"
"            err = fputc(data[i], fp);\n"
"            if (err == EOF)\n"
"                return -1;\n"
"            err = fprintf(fp, &quot;]]&gt;&quot;);\n"
"            if (err &lt; 0)\n"
"                return  -1;\n"
"            err = fprintf(fp, &quot;&lt;![CDATA[&quot;);\n"
"            if (err &lt; 0)\n"
"                return -1;\n"
"        }\n"
"    }\n"
"    err = fprintf(fp, &quot;]]&gt;&quot;);\n"
"    if (err &lt; 0)\n"
"        return -1;\n"
"    return 0;\n"
"}\n"
"\n"
"/*\n"
" The functions\n"
" \n"
" unsigned char *uudecodestr(const char *uucode, int *N)\n"
" char *uuencodestr(const unsigned char *binary, int N)\n"
" \n"
" Have been modifed by Malcolm McLean from the functions\n"
" \n"
" void UUEncode( char * pSrcFilename, FILE * pSrcFile, FILE * pDstFile )\n"
" bool UUDecode( FILE * pSrcFile )\n"
" \n"
" by Tom Weatherhead.\n"
" \n"
" They appear in Tom&apos;s github project &quot;helix&quot;.\n"
" \n"
"The MIT License (MIT)\n"
"\n"
"Copyright (c) 2002-2021 Tom Weatherhead\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the &quot;Software&quot;), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
"SOFTWARE.\n"
" \n"
" */\n"
"/*\n"
"    decode a uuencoded string to binary\n"
" */\n"
"unsigned char *uudecodestr(const char *uucode, int *N)\n"
"{\n"
"    int Nmaxout;\n"
"    unsigned char *out;\n"
"    int ix = 0;\n"
"    int jx = 0;\n"
"    int k;\n"
"    int i;\n"
"    \n"
"    Nmaxout = ((int)strlen(uucode)/60 +1) * 45;\n"
"    out = malloc(Nmaxout);\n"
"    \n"
"    while( uucode[ix] )\n"
"    {\n"
"        // Get a line of the src text file.\n"
"        char acSrcData[128];\n"
"        \n"
"        k = 0;\n"
"        while (uucode[ix] != &apos;\\n&apos; &amp;&amp; uucode[ix])\n"
"        {\n"
"            acSrcData[k++] = uucode[ix++];\n"
"            if (k &gt; 126)\n"
"                goto error_exit;\n"
"        }\n"
"        acSrcData[k++] = 0;\n"
"        if (uucode[ix])\n"
"            ix++;\n"
"        if( strlen( acSrcData ) == 0 )\n"
"        {\n"
"            // continue;\n"
"        }\n"
"        else if( acSrcData[0] &gt; 32  &amp;&amp;  acSrcData[0] &lt;= 77 )\n"
"        {\n"
"            int knBytesToWrite = (int)( acSrcData[0] - 32 );\n"
"            unsigned char acDstData[45];\n"
"            int nDstIndex = 0;\n"
"\n"
"            for( int i = 1; i &lt; strlen( acSrcData ); i += 4 )\n"
"            {\n"
"                int j;\n"
"                int anSrc1[4];\n"
"\n"
"                for( j = 0; j &lt; 4; ++j )\n"
"                {\n"
"                    const char c = acSrcData[i + j];\n"
"\n"
"                    if( c &lt; 32  ||  c &gt; 96 )\n"
"                    {\n"
"                        break;\n"
"                    }\n"
"\n"
"                    anSrc1[j] = (int)( ( c - 32 ) &amp; 63);\n"
"                }\n"
"\n"
"                for( ; j &lt; 4; ++j )\n"
"                {\n"
"                    anSrc1[j] = 0;\n"
"                }\n"
"\n"
"                int k = 0;\n"
"\n"
"                for( j = 0; j &lt; 4; ++j )\n"
"                {\n"
"                    k |= ( anSrc1[j] &lt;&lt; ( j * 6 ) );\n"
"                }\n"
"\n"
"                for( j = 0; j &lt; 3; ++j )\n"
"                {\n"
"                    acDstData[nDstIndex++] = (unsigned char)( k &gt;&gt; ( j * 8 ) );\n"
"                }\n"
"            }\n"
"\n"
"            for (i = 0; i &lt; knBytesToWrite; i++)\n"
"                out[jx++] = acDstData[i];\n"
"        }\n"
"    }\n"
"    \n"
"    if (N)\n"
"        *N = jx;\n"
"\n"
"    return out;\n"
"    \n"
"error_exit:\n"
"    free(out);\n"
"    if (N)\n"
"        *N = -1;\n"
"    return 0;\n"
"}\n"
"\n"
"/*\n"
"    encode binary to text using uuencoding.\n"
" */\n"
"char *uuencodestr(const unsigned char *binary, int N)\n"
"{\n"
"    char acTable[64];\n"
"    int i;\n"
"    int j = 0;\n"
"    char *out = 0;\n"
"    int Nleft;\n"
"    int Nout;\n"
"    \n"
"    acTable[0] = &apos;`&apos;;    // We could use a space instead.\n"
"\n"
"    for( i = 1; i &lt; 64; ++i )\n"
"    {\n"
"        acTable[i] = (char)( i + 32 );\n"
"    }\n"
"    \n"
"    Nout = (N/45 + 1) * (15 * 4 + 1 + 1);\n"
"    out = malloc(Nout + 1);\n"
"    if (!out)\n"
"        goto out_of_memory;\n"
"    \n"
"    Nleft = N;\n"
"    while( Nleft &gt; 0 )\n"
"    {\n"
"        unsigned char aucSrcData[45];\n"
"        int knBytesRead = Nleft &gt; 45 \? 45 : Nleft;\n"
"\n"
"        memcpy(aucSrcData, binary + N - Nleft, knBytesRead);\n"
"        \n"
"        out[j++] = acTable[knBytesRead];\n"
"\n"
"        for( i = 0; i &lt; knBytesRead; i += 3 )\n"
"        {\n"
"            unsigned long k = (unsigned int)aucSrcData[i]\n"
"+ ( (unsigned long)aucSrcData[i + 1] &lt;&lt; 8 ) + ( (unsigned long)aucSrcData[i\n"
"+ 2] &lt;&lt; 16 );\n"
"\n"
"            out[j++] = acTable[k &amp; 63];\n"
"            out[j++] = acTable[(k &gt;&gt; 6) &amp; 63];\n"
"            out[j++] = acTable[(k &gt;&gt; 12) &amp; 63];\n"
"            out[j++] = acTable[k &gt;&gt; 18];\n"
"        }\n"
"        out[j++] = &apos;\\n&apos;;\n"
"        Nleft -= knBytesRead;\n"
"    }\n"
"    out[j++] = 0;\n"
"    \n"
"    return out;\n"
"    \n"
"out_of_memory:\n"
"    return 0;\n"
"}\n"
"\n"
"\n"
"int writetextfile(FILE *fpout, const char *fname)\n"
"{\n"
"    FILE *fp = 0;\n"
"    char *text = 0;\n"
"    char *xmltext = 0;\n"
"    int i;\n"
"\n"
"    fp = fopen(fname, &quot;r&quot;);\n"
"    text = fslurp(fp);\n"
"    if (!text)\n"
"        goto out_of_memory;\n"
"    fclose(fp);\n"
"    fp = 0;\n"
"    \n"
"    xmltext = xml_escape(text);\n"
"    if (!xmltext)\n"
"        goto out_of_memory;\n"
"    for (i=0; xmltext[i];i++)\n"
"        fputc(xmltext[i], fpout);\n"
"    \n"
"    free(text);\n"
"    free(xmltext);\n"
"    \n"
"    return 0;\n"
"    \n"
"out_of_memory:\n"
"    fclose(fp);\n"
"    free(text);\n"
"    free(xmltext);\n"
"    \n"
"    return 0;\n"
"}\n"
"\n"
"int writebinaryfile(FILE *fpout, const char *fname)\n"
"{\n"
"    int N;\n"
"    unsigned char *binary = 0;\n"
"    char *uucode = 0;\n"
"    char *badclose;\n"
"    int i;\n"
"    \n"
"    binary = slurpb(fname, &amp;N);\n"
"    if (!binary)\n"
"        goto out_of_memory;\n"
"    uucode = uuencodestr(binary,N);\n"
"    if (!uucode)\n"
"        goto out_of_memory;\n"
"    xml_writecdata(fpout, uucode);\n"
"    free(binary);\n"
"    free(uucode);\n"
"    \n"
"    return 0;\n"
"out_of_memory:\n"
"    free(binary);\n"
"    free(uucode);\n"
"    return -1;\n"
"}\n"
"\n"
"void processregularfile(const char *path, int depth)\n"
"{\n"
"    int error;\n"
"    char *filename = 0;\n"
"    char *xmlfilename = 0;\n"
"    int i;\n"
"    \n"
"    filename = getfilename(path);\n"
"    if (!filename)\n"
"        goto out_of_memory;\n"
"    xmlfilename = xml_escape(filename);\n"
"    if (!xmlfilename)\n"
"        goto out_of_memory;\n"
"    \n"
"    \n"
"    if (!is_binary(path))\n"
"    {\n"
"        for (i = 0; i &lt;depth; i++)\n"
"            printf(&quot;\\t&quot;);\n"
"        printf(&quot;&lt;file name=\\&quot;%s\\&quot; type=\\&quot;text\\&quot;&gt;\\n&quot;, xmlfilename"
");\n"
"        writetextfile(stdout, path);\n"
"        printf(&quot;\\n&quot;);\n"
"        for (i= 0; i &lt;depth;i++)\n"
"            printf(&quot;\\t&quot;);\n"
"        printf(&quot;&lt;/file&gt;\\n&quot;);\n"
"    }\n"
"    else\n"
"    {\n"
"        for (i = 0; i &lt;depth; i++)\n"
"            printf(&quot;\\t&quot;);\n"
"        printf(&quot;&lt;file name=\\&quot;%s\\&quot; type=\\&quot;binary\\&quot;&gt;\\n&quot;, xmlfilena"
"me);\n"
"        writebinaryfile(stdout, path);\n"
"        printf(&quot;\\n&quot;);\n"
"        for (i= 0; i &lt;depth;i++)\n"
"            printf(&quot;\\t&quot;);\n"
"        printf(&quot;&lt;/file&gt;\\n&quot;);\n"
"    }\n"
"    free(filename);\n"
"    free(xmlfilename);\n"
"    return;\n"
"    \n"
"out_of_memory:\n"
"    free(filename);\n"
"    free(xmlfilename);\n"
"}\n"
"\n"
"void processdirectory_r(const char *path, int depth)\n"
"{\n"
"    DIR *dirp;\n"
"    struct dirent *dp;\n"
"    char *pathslash;\n"
"    char *filepath;\n"
"    char *xmlfilename;\n"
"    int i;\n"
"\n"
"    pathslash = mystrconcat(path, &quot;/&quot;);\n"
"    if (!pathslash)\n"
"        goto out_of_memory;\n"
"    \n"
"    if ((dirp = opendir(path)) == NULL) {\n"
"        perror(&quot;couldn&apos;t open directory&quot;);\n"
"        return;\n"
"    }\n"
"\n"
"\n"
"    do {\n"
"        errno = 0;\n"
"        if ((dp = readdir(dirp)) != NULL) {\n"
"          filepath = mystrconcat(pathslash, dp-&gt;d_name);\n"
"          if (is_directory(filepath) &amp;&amp; dp-&gt;d_name[0] != &apos;.&apos;)\n"
"          {\n"
"              xmlfilename = xml_escape(dp-&gt;d_name);\n"
"              if (!xmlfilename)\n"
"                  goto out_of_memory;\n"
"              for (i = 0; i &lt; depth; i++)\n"
"                      printf(&quot;\\t&quot;);\n"
"              printf(&quot;&lt;directory name=\\&quot;%s\\&quot;&gt;\\n&quot;, xmlfilename);\n"
"              processdirectory_r(filepath, depth +1);\n"
"              for (i = 0; i &lt; depth; i++)\n"
"                      printf(&quot;\\t&quot;);\n"
"              printf(&quot;&lt;/directory&gt;\\n&quot;);\n"
"              free(xmlfilename);\n"
"              xmlfilename = 0;\n"
"          }\n"
"          else if (dp-&gt;d_name[0] != &apos;.&apos; &amp;&amp; is_regular_file(filepath))\n"
"                processregularfile(filepath, depth);\n"
"          free(filepath);\n"
"        }\n"
"    } while (dp != NULL);\n"
"\n"
"    if (errno != 0)\n"
"        perror(&quot;error reading directory&quot;);\n"
"\n"
"    free(pathslash);\n"
"    closedir(dirp);\n"
"    return;\n"
"out_of_memory:\n"
"    free(pathslash);\n"
"    closedir(dirp);\n"
"}\n"
"\n"
"int directorytoxml(const char *directory)\n"
"{\n"
"    char *filename = 0 ;\n"
"    char *xmlfilename = 0;\n"
"    \n"
"    if (!is_directory(directory))\n"
"    {\n"
"        fprintf(stderr, &quot;Can&apos;t open directory %s\\n&quot;, directory);\n"
"        return -1;\n"
"    }\n"
"    \n"
"    filename = getfilename(directory);\n"
"    if (!filename)\n"
"        goto out_of_memory;\n"
"    xmlfilename = xml_escape(filename);\n"
"    if (!xmlfilename)\n"
"        goto out_of_memory;\n"
"    \n"
"    printf(&quot;&lt;FileSystem&gt;\\n&quot;);\n"
"    printf(&quot;\\t&lt;directory name=\\&quot;%s\\&quot;&gt;\\n&quot;, xmlfilename);\n"
"    processdirectory_r(directory, 2);\n"
"    printf(&quot;\\t&lt;/directory&gt;\\n&quot;);\n"
"    printf(&quot;&lt;/FileSystem&gt;\\n&quot;);\n"
"    \n"
"    free(filename);\n"
"    free(xmlfilename);\n"
"    return 0;\n"
"out_of_memory:\n"
"    free(filename);\n"
"    free(xmlfilename);\n"
"    \n"
"    return -1;\n"
"}\n"
"\n"
"void usage(void)\n"
"{\n"
"    fprintf(stderr, &quot;directorytoxml: converts a directory to an xml file\\n&quot;);\n"
"    fprintf(stderr, &quot;Usage: directorytoxml &lt;directory&gt;\\n&quot;);\n"
"    fprintf(stderr, &quot;\\n&quot;);\n"
"    fprintf(stderr, &quot;by Malcolm McLean\\n&quot;);\n"
"    fprintf(stderr, &quot;For use with the program directory to query the XML for files\\n&quot;);\n"
"}\n"
"\n"
"int main(int argc, char **argv)\n"
"{\n"
"    int error;\n"
"    \n"
"    if (argc == 1)\n"
"        error = directorytoxml(&quot;.&quot;);\n"
"    else if (argc == 2)\n"
"        error = directorytoxml(argv[1]);\n"
"    else\n"
"        error = -1;\n"
"    \n"
"    if (error)\n"
"        usage();\n"
"    return 0;\n"
"}\n"
"\n"
"\t\t</file>\n"
"\t\t<file name=\"bbx_filesystem.h\" type=\"text\">\n"
"//\n"
"//  bbx_filesystem.h\n"
"//  babyxrc\n"
"//\n"
"//  Created by Malcolm McLean on 31/05/2024.\n"
"//\n"
"\n"
"#ifndef bbx_filesystem_h\n"
"#define bbx_filesystem_h\n"
"\n"
"#define BBX_FS_STDIO 1\n"
"#define BBX_FS_STRING 2\n"
"\n"
"typedef struct bbx_filesystem BBX_FileSystem;\n"
"\n"
"BBX_FileSystem *bbx_filesystem(void);\n"
"void bbx_filesystem_kill(BBX_FileSystem *bbx_fs);\n"
"int bbx_filesystem_set(BBX_FileSystem *bbx_fs, const char *pathorxml, int mode);\n"
"FILE *bbx_filesystem_fopen(BBX_FileSystem *bbx_fs, const char *path, const char *mode);\n"
"int bbx_filesystem_fclose(BBX_FileSystem *bbx_fs, FILE *fp);\n"
"const char *bbx_filesystem_getname(BBX_FileSystem *bbx_fs);\n"
"\n"
"#endif /* bbx_filesystem_h */\n"
"\n"
"\t\t</file>\n"
"\t</directory>\n"
"</FileSystem>\n";
